Merge branch 'dev' into main
This commit is contained in:
@@ -3,6 +3,7 @@ package com.sb.web.auth.controller;
|
|||||||
import com.sb.web.auth.dto.LoginRequest;
|
import com.sb.web.auth.dto.LoginRequest;
|
||||||
import com.sb.web.auth.dto.LoginResponse;
|
import com.sb.web.auth.dto.LoginResponse;
|
||||||
import com.sb.web.auth.dto.MemberResponse;
|
import com.sb.web.auth.dto.MemberResponse;
|
||||||
|
import com.sb.web.auth.dto.PasswordChangeRequest;
|
||||||
import com.sb.web.auth.dto.SessionUser;
|
import com.sb.web.auth.dto.SessionUser;
|
||||||
import com.sb.web.auth.dto.SignupRequest;
|
import com.sb.web.auth.dto.SignupRequest;
|
||||||
import com.sb.web.auth.service.AuthService;
|
import com.sb.web.auth.service.AuthService;
|
||||||
@@ -48,4 +49,12 @@ public class AuthController {
|
|||||||
public SessionUser me(@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
public SessionUser me(@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PutMapping("/password")
|
||||||
|
public ResponseEntity<Void> changePassword(
|
||||||
|
@Valid @RequestBody PasswordChangeRequest req,
|
||||||
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
||||||
|
authService.changePassword(current.getId(), req);
|
||||||
|
return ResponseEntity.noContent().build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.sb.web.auth.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 비밀번호 변경 요청 (로그인 사용자 본인).
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class PasswordChangeRequest {
|
||||||
|
|
||||||
|
@NotBlank(message = "현재 비밀번호를 입력하세요.")
|
||||||
|
private String currentPassword;
|
||||||
|
|
||||||
|
@NotBlank(message = "새 비밀번호를 입력하세요.")
|
||||||
|
@Size(min = 8, max = 64, message = "비밀번호는 8~64자여야 합니다.")
|
||||||
|
private String newPassword;
|
||||||
|
}
|
||||||
@@ -20,4 +20,6 @@ public interface MemberMapper {
|
|||||||
int countByLoginId(@Param("loginId") String loginId);
|
int countByLoginId(@Param("loginId") String loginId);
|
||||||
|
|
||||||
int insert(Member member);
|
int insert(Member member);
|
||||||
|
|
||||||
|
int updatePassword(@Param("id") Long id, @Param("password") String password);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.sb.web.auth.domain.Member;
|
|||||||
import com.sb.web.auth.dto.LoginRequest;
|
import com.sb.web.auth.dto.LoginRequest;
|
||||||
import com.sb.web.auth.dto.LoginResponse;
|
import com.sb.web.auth.dto.LoginResponse;
|
||||||
import com.sb.web.auth.dto.MemberResponse;
|
import com.sb.web.auth.dto.MemberResponse;
|
||||||
|
import com.sb.web.auth.dto.PasswordChangeRequest;
|
||||||
import com.sb.web.auth.dto.SessionUser;
|
import com.sb.web.auth.dto.SessionUser;
|
||||||
import com.sb.web.auth.dto.SignupRequest;
|
import com.sb.web.auth.dto.SignupRequest;
|
||||||
import com.sb.web.common.exception.ApiException;
|
import com.sb.web.common.exception.ApiException;
|
||||||
@@ -99,4 +100,26 @@ public class AuthService {
|
|||||||
redisTemplate.delete(SESSION_PREFIX + token);
|
redisTemplate.delete(SESSION_PREFIX + token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 비밀번호 변경 (본인). 현재 비밀번호 검증 후 새 비밀번호로 교체.
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public void changePassword(Long memberId, PasswordChangeRequest req) {
|
||||||
|
Member member = memberMapper.findById(memberId);
|
||||||
|
if (member == null) {
|
||||||
|
throw new ApiException(HttpStatus.NOT_FOUND, "회원을 찾을 수 없습니다.");
|
||||||
|
}
|
||||||
|
if (!"LOCAL".equals(member.getProvider()) || member.getPassword() == null) {
|
||||||
|
throw new ApiException(HttpStatus.BAD_REQUEST, "소셜 로그인 계정은 비밀번호를 변경할 수 없습니다.");
|
||||||
|
}
|
||||||
|
if (!passwordEncoder.matches(req.getCurrentPassword(), member.getPassword())) {
|
||||||
|
throw new ApiException(HttpStatus.BAD_REQUEST, "현재 비밀번호가 올바르지 않습니다.");
|
||||||
|
}
|
||||||
|
if (passwordEncoder.matches(req.getNewPassword(), member.getPassword())) {
|
||||||
|
throw new ApiException(HttpStatus.BAD_REQUEST, "새 비밀번호가 기존 비밀번호와 동일합니다.");
|
||||||
|
}
|
||||||
|
memberMapper.updatePassword(memberId, passwordEncoder.encode(req.getNewPassword()));
|
||||||
|
log.info("[password] changed for {}", member.getLoginId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ public class WebConfig implements WebMvcConfigurer {
|
|||||||
public void addInterceptors(InterceptorRegistry registry) {
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
// 인증: 로그인 필요한 경로 (관리자 경로 포함 — SessionUser 세팅용으로 먼저 실행)
|
// 인증: 로그인 필요한 경로 (관리자 경로 포함 — SessionUser 세팅용으로 먼저 실행)
|
||||||
registry.addInterceptor(authInterceptor)
|
registry.addInterceptor(authInterceptor)
|
||||||
.addPathPatterns("/api/auth/me", "/api/auth/logout", "/api/board/**", "/api/admin/**", "/api/account/**");
|
.addPathPatterns("/api/auth/me", "/api/auth/logout", "/api/auth/password",
|
||||||
|
"/api/board/**", "/api/admin/**", "/api/account/**");
|
||||||
// 인가: 관리자 전용 경로 (authInterceptor 다음에 실행되어 role 검사)
|
// 인가: 관리자 전용 경로 (authInterceptor 다음에 실행되어 role 검사)
|
||||||
registry.addInterceptor(adminInterceptor)
|
registry.addInterceptor(adminInterceptor)
|
||||||
.addPathPatterns("/api/admin/**");
|
.addPathPatterns("/api/admin/**");
|
||||||
|
|||||||
@@ -46,4 +46,8 @@
|
|||||||
#{provider}, #{providerId}, #{role}, #{status}, NOW(), NOW())
|
#{provider}, #{providerId}, #{role}, #{status}, NOW(), NOW())
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
<update id="updatePassword">
|
||||||
|
UPDATE member SET password = #{password}, updated_at = NOW() WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
Reference in New Issue
Block a user