spotlessApply 적용

This commit is contained in:
2025-12-11 13:47:57 +09:00
parent ec19cf533a
commit d798dc16f9
14 changed files with 261 additions and 279 deletions

View File

@@ -28,9 +28,9 @@ public class CustomAuthenticationProvider implements AuthenticationProvider {
// 1. 유저 조회 // 1. 유저 조회
MemberEntity member = MemberEntity member =
membersRepository membersRepository
.findByUserId(username) .findByUserId(username)
.orElseThrow(() -> new CustomApiException(AuthErrorCode.LOGIN_ID_NOT_FOUND)); .orElseThrow(() -> new CustomApiException(AuthErrorCode.LOGIN_ID_NOT_FOUND));
// 2. jBCrypt + 커스텀 salt 로 저장된 패스워드 비교 // 2. jBCrypt + 커스텀 salt 로 저장된 패스워드 비교
if (!BCrypt.checkpw(rawPassword, member.getPassword())) { if (!BCrypt.checkpw(rawPassword, member.getPassword())) {

View File

@@ -219,7 +219,7 @@ public class CommonCodeApiController {
// .map(Clazzes::new) // .map(Clazzes::new)
// .toList(); // .toList();
//변화탐지 clazz API : enum -> 공통코드로 변경 // 변화탐지 clazz API : enum -> 공통코드로 변경
List<CommonCodeDto.Clazzes> list = List<CommonCodeDto.Clazzes> list =
commonCodeUtil.getChildCodesByParentCode("0000").stream() commonCodeUtil.getChildCodesByParentCode("0000").stream()
.map( .map(

View File

@@ -8,17 +8,11 @@ import org.springframework.http.HttpStatus;
public enum AuthErrorCode implements ErrorCode { public enum AuthErrorCode implements ErrorCode {
// 🔐 로그인 관련 // 🔐 로그인 관련
LOGIN_ID_NOT_FOUND( LOGIN_ID_NOT_FOUND("LOGIN_ID_NOT_FOUND", HttpStatus.UNAUTHORIZED),
"LOGIN_ID_NOT_FOUND",
HttpStatus.UNAUTHORIZED),
LOGIN_PASSWORD_MISMATCH( LOGIN_PASSWORD_MISMATCH("LOGIN_PASSWORD_MISMATCH", HttpStatus.UNAUTHORIZED),
"LOGIN_PASSWORD_MISMATCH",
HttpStatus.UNAUTHORIZED),
LOGIN_PASSWORD_EXCEEDED( LOGIN_PASSWORD_EXCEEDED("LOGIN_PASSWORD_EXCEEDED", HttpStatus.UNAUTHORIZED);
"LOGIN_PASSWORD_EXCEEDED",
HttpStatus.UNAUTHORIZED);
private final String code; private final String code;
private final HttpStatus status; private final HttpStatus status;

View File

@@ -30,33 +30,33 @@ public class SecurityConfig {
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.cors(cors -> cors.configurationSource(corsConfigurationSource())) http.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.csrf(csrf -> csrf.disable()) // CSRF 보안 기능 비활성화 .csrf(csrf -> csrf.disable()) // CSRF 보안 기능 비활성화
.sessionManagement( .sessionManagement(
sm -> sm ->
sm.sessionCreationPolicy( sm.sessionCreationPolicy(
SessionCreationPolicy.STATELESS)) // 서버 세션 만들지 않음, 요청은 JWT 인증 SessionCreationPolicy.STATELESS)) // 서버 세션 만들지 않음, 요청은 JWT 인증
.formLogin(form -> form.disable()) // react에서 로그인 요청 관리 .formLogin(form -> form.disable()) // react에서 로그인 요청 관리
.httpBasic(basic -> basic.disable()) // 기본 basic 인증 비활성화 JWT 인증사용 .httpBasic(basic -> basic.disable()) // 기본 basic 인증 비활성화 JWT 인증사용
.logout(logout -> logout.disable()) // 기본 로그아웃 비활성화 JWT는 서버 상태가 없으므로 로그아웃 처리 필요 없음 .logout(logout -> logout.disable()) // 기본 로그아웃 비활성화 JWT는 서버 상태가 없으므로 로그아웃 처리 필요 없음
.authenticationProvider( .authenticationProvider(
customAuthenticationProvider) // 로그인 패스워드 비교방식 스프링 기본 Provider 사용안함 커스텀 사용 customAuthenticationProvider) // 로그인 패스워드 비교방식 스프링 기본 Provider 사용안함 커스텀 사용
.authorizeHttpRequests( .authorizeHttpRequests(
auth -> auth ->
auth.requestMatchers(HttpMethod.OPTIONS, "/**") auth.requestMatchers(HttpMethod.OPTIONS, "/**")
.permitAll() // preflight 허용 .permitAll() // preflight 허용
.requestMatchers( .requestMatchers(
"/api/auth/signin", "/api/auth/signin",
"/api/auth/refresh", "/api/auth/refresh",
"/swagger-ui/**", "/swagger-ui/**",
"/api/members/{memberId}/password", "/api/members/{memberId}/password",
"/v3/api-docs/**") "/v3/api-docs/**")
.permitAll() .permitAll()
.anyRequest() .anyRequest()
.authenticated()) .authenticated())
.addFilterBefore( .addFilterBefore(
jwtAuthenticationFilter, jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter UsernamePasswordAuthenticationFilter
.class) // 요청 들어오면 먼저 JWT 토큰 검사 후 security context 에 사용자 정보 저장. .class) // 요청 들어오면 먼저 JWT 토큰 검사 후 security context 에 사용자 정보 저장.
; ;
return http.build(); return http.build();
@@ -64,7 +64,7 @@ public class SecurityConfig {
@Bean @Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration)
throws Exception { throws Exception {
return configuration.getAuthenticationManager(); return configuration.getAuthenticationManager();
} }

View File

@@ -20,11 +20,9 @@ public class ApiResponseDto<T> {
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
private T errorData; private T errorData;
@JsonIgnore @JsonIgnore private HttpStatus httpStatus;
private HttpStatus httpStatus;
@JsonIgnore @JsonIgnore private Long errorLogUid;
private Long errorLogUid;
public ApiResponseDto(T data) { public ApiResponseDto(T data) {
this.data = data; this.data = data;
@@ -49,7 +47,7 @@ public class ApiResponseDto<T> {
} }
public ApiResponseDto( public ApiResponseDto(
ApiResponseCode code, String message, HttpStatus httpStatus, Long errorLogUid) { ApiResponseCode code, String message, HttpStatus httpStatus, Long errorLogUid) {
this.error = new Error(code.getId(), message); this.error = new Error(code.getId(), message);
this.httpStatus = httpStatus; this.httpStatus = httpStatus;
this.errorLogUid = errorLogUid; this.errorLogUid = errorLogUid;
@@ -90,17 +88,17 @@ public class ApiResponseDto<T> {
} }
public static ApiResponseDto<String> createException( public static ApiResponseDto<String> createException(
ApiResponseCode code, String message, HttpStatus httpStatus) { ApiResponseCode code, String message, HttpStatus httpStatus) {
return new ApiResponseDto<>(code, message, httpStatus); return new ApiResponseDto<>(code, message, httpStatus);
} }
public static ApiResponseDto<String> createException( public static ApiResponseDto<String> createException(
ApiResponseCode code, String message, HttpStatus httpStatus, Long errorLogUid) { ApiResponseCode code, String message, HttpStatus httpStatus, Long errorLogUid) {
return new ApiResponseDto<>(code, message, httpStatus, errorLogUid); return new ApiResponseDto<>(code, message, httpStatus, errorLogUid);
} }
public static <T> ApiResponseDto<T> createException( public static <T> ApiResponseDto<T> createException(
ApiResponseCode code, String message, T data) { ApiResponseCode code, String message, T data) {
return new ApiResponseDto<>(code, message, data); return new ApiResponseDto<>(code, message, data);
} }
@@ -116,9 +114,7 @@ public class ApiResponseDto<T> {
} }
} }
/** /** Error가 아닌 Business상 성공이거나 실패인 경우, 메세지 함께 전달하기 위한 object */
* Error가 아닌 Business상 성공이거나 실패인 경우, 메세지 함께 전달하기 위한 object
*/
@Getter @Getter
public static class ResponseObj { public static class ResponseObj {

View File

@@ -30,78 +30,78 @@ public class AdminApiController {
@Operation(summary = "관리자 계정 등록", description = "관리자 계정 등록") @Operation(summary = "관리자 계정 등록", description = "관리자 계정 등록")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
responseCode = "201", responseCode = "201",
description = "등록 성공", description = "등록 성공",
content = content =
@Content( @Content(
mediaType = "application/json", mediaType = "application/json",
schema = @Schema(implementation = Long.class))), schema = @Schema(implementation = Long.class))),
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content), @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content),
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
}) })
@PostMapping("/join") @PostMapping("/join")
public ApiResponseDto<Long> saveMember( public ApiResponseDto<Long> saveMember(
@io.swagger.v3.oas.annotations.parameters.RequestBody( @io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "관리자 계정 등록", description = "관리자 계정 등록",
required = true, required = true,
content = content =
@Content( @Content(
mediaType = "application/json", mediaType = "application/json",
schema = @Schema(implementation = MembersDto.AddReq.class))) schema = @Schema(implementation = MembersDto.AddReq.class)))
@RequestBody @RequestBody
@Valid @Valid
MembersDto.AddReq addReq) { MembersDto.AddReq addReq) {
return ApiResponseDto.createOK(adminService.saveMember(addReq)); return ApiResponseDto.createOK(adminService.saveMember(addReq));
} }
@Operation(summary = "관리자 계정 수정", description = "관리자 계정 수정") @Operation(summary = "관리자 계정 수정", description = "관리자 계정 수정")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
responseCode = "201", responseCode = "201",
description = "수정 성공", description = "수정 성공",
content = content =
@Content( @Content(
mediaType = "application/json", mediaType = "application/json",
schema = @Schema(implementation = Long.class))), schema = @Schema(implementation = Long.class))),
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content), @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content),
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
}) })
@PutMapping("/{uuid}") @PutMapping("/{uuid}")
public ApiResponseDto<UUID> updateMembers( public ApiResponseDto<UUID> updateMembers(
@io.swagger.v3.oas.annotations.parameters.RequestBody( @io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "관리자 계정 수정", description = "관리자 계정 수정",
required = true, required = true,
content = content =
@Content( @Content(
mediaType = "application/json", mediaType = "application/json",
schema = @Schema(implementation = MembersDto.UpdateReq.class))) schema = @Schema(implementation = MembersDto.UpdateReq.class)))
@PathVariable @PathVariable
UUID uuid, UUID uuid,
@RequestBody @Valid MembersDto.UpdateReq updateReq) { @RequestBody @Valid MembersDto.UpdateReq updateReq) {
adminService.updateMembers(uuid, updateReq); adminService.updateMembers(uuid, updateReq);
return ApiResponseDto.createOK(UUID.randomUUID()); return ApiResponseDto.createOK(UUID.randomUUID());
} }
@Operation(summary = "관리자 계정 미사용 처리", description = "관리자 계정 미사용 처리") @Operation(summary = "관리자 계정 미사용 처리", description = "관리자 계정 미사용 처리")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
responseCode = "201", responseCode = "201",
description = "관리자 계정 미사용 처리", description = "관리자 계정 미사용 처리",
content = content =
@Content( @Content(
mediaType = "application/json", mediaType = "application/json",
schema = @Schema(implementation = UUID.class))), schema = @Schema(implementation = UUID.class))),
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content), @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content),
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
}) })
@DeleteMapping("/delete/{uuid}") @DeleteMapping("/delete/{uuid}")
public ApiResponseDto<UUID> deleteAccount(@PathVariable UUID uuid) { public ApiResponseDto<UUID> deleteAccount(@PathVariable UUID uuid) {
adminService.deleteAccount(uuid); adminService.deleteAccount(uuid);

View File

@@ -50,57 +50,59 @@ public class AuthController {
@Operation(summary = "로그인", description = "사번으로 로그인하여 액세스/리프레시 토큰을 발급.") @Operation(summary = "로그인", description = "사번으로 로그인하여 액세스/리프레시 토큰을 발급.")
@ApiResponses({ @ApiResponses({
@ApiResponse( @ApiResponse(
responseCode = "200", responseCode = "200",
description = "로그인 성공", description = "로그인 성공",
content = @Content(schema = @Schema(implementation = TokenResponse.class))), content = @Content(schema = @Schema(implementation = TokenResponse.class))),
@ApiResponse( @ApiResponse(
responseCode = "401", responseCode = "401",
description = "로그인 실패 (아이디/비밀번호 오류, 계정잠금 등)", description = "로그인 실패 (아이디/비밀번호 오류, 계정잠금 등)",
content = content =
@Content( @Content(
schema = @Schema(implementation = ErrorResponse.class), schema = @Schema(implementation = ErrorResponse.class),
examples = { examples = {
@ExampleObject( @ExampleObject(
name = "아이디 입력 오류", name = "아이디 입력 오류",
description = "존재하지 않는 아이디", description = "존재하지 않는 아이디",
value = """ value =
"""
{ {
"code": "LOGIN_ID_NOT_FOUND", "code": "LOGIN_ID_NOT_FOUND",
"message": "아이디를 잘못 입력하셨습니다." "message": "아이디를 잘못 입력하셨습니다."
} }
"""), """),
@ExampleObject( @ExampleObject(
name = "비밀번호 입력 오류 (4회 이하)", name = "비밀번호 입력 오류 (4회 이하)",
description = "아이디는 정상, 비밀번호를 여러 번 틀린 경우", description = "아이디는 정상, 비밀번호를 여러 번 틀린 경우",
value = """ value =
"""
{ {
"code": "LOGIN_PASSWORD_MISMATCH", "code": "LOGIN_PASSWORD_MISMATCH",
"message": "비밀번호를 잘못 입력하셨습니다." "message": "비밀번호를 잘못 입력하셨습니다."
} }
"""), """),
@ExampleObject( @ExampleObject(
name = "비밀번호 오류 횟수 초과", name = "비밀번호 오류 횟수 초과",
description = "비밀번호 5회 이상 오류로 계정 잠김", description = "비밀번호 5회 이상 오류로 계정 잠김",
value = """ value =
"""
{ {
"code": "LOGIN_PASSWORD_EXCEEDED", "code": "LOGIN_PASSWORD_EXCEEDED",
"message": "비밀번호 오류 횟수를 초과하여 이용하실 수 없습니다. 로그인 오류에 대해 관리자에게 문의하시기 바랍니다." "message": "비밀번호 오류 횟수를 초과하여 이용하실 수 없습니다. 로그인 오류에 대해 관리자에게 문의하시기 바랍니다."
} }
""") """)
} }))
))
}) })
public ApiResponseDto<TokenResponse> signin( public ApiResponseDto<TokenResponse> signin(
@io.swagger.v3.oas.annotations.parameters.RequestBody( @io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "로그인 요청 정보", description = "로그인 요청 정보",
required = true) required = true)
@RequestBody @RequestBody
SignInRequest request, SignInRequest request,
HttpServletResponse response) { HttpServletResponse response) {
Authentication authentication = Authentication authentication =
authenticationManager.authenticate( authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())); new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));
String status = authService.getUserStatus(request); String status = authService.getUserStatus(request);
@@ -116,17 +118,17 @@ public class AuthController {
// Redis에 RefreshToken 저장 (TTL = 7일) // Redis에 RefreshToken 저장 (TTL = 7일)
refreshTokenService.save( refreshTokenService.save(
username, refreshToken, jwtTokenProvider.getRefreshTokenValidityInMs()); username, refreshToken, jwtTokenProvider.getRefreshTokenValidityInMs());
// HttpOnly + Secure 쿠키에 RefreshToken 저장 // HttpOnly + Secure 쿠키에 RefreshToken 저장
ResponseCookie cookie = ResponseCookie cookie =
ResponseCookie.from(refreshCookieName, refreshToken) ResponseCookie.from(refreshCookieName, refreshToken)
.httpOnly(true) .httpOnly(true)
.secure(refreshCookieSecure) .secure(refreshCookieSecure)
.path("/") .path("/")
.maxAge(Duration.ofMillis(jwtTokenProvider.getRefreshTokenValidityInMs())) .maxAge(Duration.ofMillis(jwtTokenProvider.getRefreshTokenValidityInMs()))
.sameSite("Strict") .sameSite("Strict")
.build(); .build();
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString()); response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
@@ -137,16 +139,16 @@ public class AuthController {
@Operation(summary = "토큰 재발급", description = "리프레시 토큰으로 새로운 액세스/리프레시 토큰을 재발급합니다.") @Operation(summary = "토큰 재발급", description = "리프레시 토큰으로 새로운 액세스/리프레시 토큰을 재발급합니다.")
@ApiResponses({ @ApiResponses({
@ApiResponse( @ApiResponse(
responseCode = "200", responseCode = "200",
description = "재발급 성공", description = "재발급 성공",
content = @Content(schema = @Schema(implementation = TokenResponse.class))), content = @Content(schema = @Schema(implementation = TokenResponse.class))),
@ApiResponse( @ApiResponse(
responseCode = "401", responseCode = "401",
description = "만료되었거나 유효하지 않은 리프레시 토큰", description = "만료되었거나 유효하지 않은 리프레시 토큰",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))) content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
}) })
public ResponseEntity<TokenResponse> refresh(String refreshToken, HttpServletResponse response) public ResponseEntity<TokenResponse> refresh(String refreshToken, HttpServletResponse response)
throws AccessDeniedException { throws AccessDeniedException {
if (refreshToken == null || !jwtTokenProvider.isValidToken(refreshToken)) { if (refreshToken == null || !jwtTokenProvider.isValidToken(refreshToken)) {
throw new AccessDeniedException("만료되었거나 유효하지 않은 리프레시 토큰 입니다."); throw new AccessDeniedException("만료되었거나 유효하지 않은 리프레시 토큰 입니다.");
} }
@@ -164,17 +166,17 @@ public class AuthController {
// Redis 갱신 // Redis 갱신
refreshTokenService.save( refreshTokenService.save(
username, newRefreshToken, jwtTokenProvider.getRefreshTokenValidityInMs()); username, newRefreshToken, jwtTokenProvider.getRefreshTokenValidityInMs());
// 쿠키 갱신 // 쿠키 갱신
ResponseCookie cookie = ResponseCookie cookie =
ResponseCookie.from(refreshCookieName, newRefreshToken) ResponseCookie.from(refreshCookieName, newRefreshToken)
.httpOnly(true) .httpOnly(true)
.secure(refreshCookieSecure) .secure(refreshCookieSecure)
.path("/") .path("/")
.maxAge(Duration.ofMillis(jwtTokenProvider.getRefreshTokenValidityInMs())) .maxAge(Duration.ofMillis(jwtTokenProvider.getRefreshTokenValidityInMs()))
.sameSite("Strict") .sameSite("Strict")
.build(); .build();
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString()); response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
return ResponseEntity.ok(new TokenResponse("ACTIVE", newAccessToken, newRefreshToken)); return ResponseEntity.ok(new TokenResponse("ACTIVE", newAccessToken, newRefreshToken));
@@ -184,12 +186,12 @@ public class AuthController {
@Operation(summary = "로그아웃", description = "현재 사용자의 토큰을 무효화(리프레시 토큰 삭제)합니다.") @Operation(summary = "로그아웃", description = "현재 사용자의 토큰을 무효화(리프레시 토큰 삭제)합니다.")
@ApiResponses({ @ApiResponses({
@ApiResponse( @ApiResponse(
responseCode = "200", responseCode = "200",
description = "로그아웃 성공", description = "로그아웃 성공",
content = @Content(schema = @Schema(implementation = Void.class))) content = @Content(schema = @Schema(implementation = Void.class)))
}) })
public ApiResponseDto<ResponseEntity<Object>> logout( public ApiResponseDto<ResponseEntity<Object>> logout(
Authentication authentication, HttpServletResponse response) { Authentication authentication, HttpServletResponse response) {
if (authentication != null) { if (authentication != null) {
String username = authentication.getName(); String username = authentication.getName();
// Redis에서 RefreshToken 삭제 // Redis에서 RefreshToken 삭제
@@ -198,19 +200,17 @@ public class AuthController {
// 쿠키 삭제 (Max-Age=0) // 쿠키 삭제 (Max-Age=0)
ResponseCookie cookie = ResponseCookie cookie =
ResponseCookie.from(refreshCookieName, "") ResponseCookie.from(refreshCookieName, "")
.httpOnly(true) .httpOnly(true)
.secure(refreshCookieSecure) .secure(refreshCookieSecure)
.path("/") .path("/")
.maxAge(0) .maxAge(0)
.sameSite("Strict") .sameSite("Strict")
.build(); .build();
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString()); response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
return ApiResponseDto.createOK(ResponseEntity.noContent().build()); return ApiResponseDto.createOK(ResponseEntity.noContent().build());
} }
public record TokenResponse(String status, String accessToken, String refreshToken) { public record TokenResponse(String status, String accessToken, String refreshToken) {}
}
} }

View File

@@ -34,43 +34,43 @@ public class MembersApiController {
@Operation(summary = "회원정보 목록", description = "회원정보 조회") @Operation(summary = "회원정보 목록", description = "회원정보 조회")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
responseCode = "200", responseCode = "200",
description = "검색 성공", description = "검색 성공",
content = content =
@Content( @Content(
mediaType = "application/json", mediaType = "application/json",
schema = @Schema(implementation = Page.class))), schema = @Schema(implementation = Page.class))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
}) })
@GetMapping @GetMapping
public ApiResponseDto<Page<Basic>> getMemberList( public ApiResponseDto<Page<Basic>> getMemberList(
@ParameterObject MembersDto.SearchReq searchReq) { @ParameterObject MembersDto.SearchReq searchReq) {
return ApiResponseDto.ok(membersService.findByMembers(searchReq)); return ApiResponseDto.ok(membersService.findByMembers(searchReq));
} }
@Operation(summary = "사용자 비밀번호 변경", description = "사용자 비밀번호 변경") @Operation(summary = "사용자 비밀번호 변경", description = "사용자 비밀번호 변경")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
responseCode = "201", responseCode = "201",
description = "사용자 비밀번호 변경", description = "사용자 비밀번호 변경",
content = content =
@Content( @Content(
mediaType = "application/json", mediaType = "application/json",
schema = @Schema(implementation = Long.class))), schema = @Schema(implementation = Long.class))),
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content), @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content),
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
}) })
@PatchMapping("/{memberId}/password") @PatchMapping("/{memberId}/password")
public ApiResponseDto<String> resetPassword(@PathVariable String memberId, @RequestBody @Valid MembersDto.InitReq initReq) { public ApiResponseDto<String> resetPassword(
@PathVariable String memberId, @RequestBody @Valid MembersDto.InitReq initReq) {
authenticationManager.authenticate( authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(memberId, initReq.getTempPassword())); new UsernamePasswordAuthenticationToken(memberId, initReq.getTempPassword()));
membersService.resetPassword(memberId, initReq); membersService.resetPassword(memberId, initReq);
return ApiResponseDto.createOK(memberId); return ApiResponseDto.createOK(memberId);

View File

@@ -32,31 +32,26 @@ public class MembersDto {
private String tempPassword; private String tempPassword;
private String status; private String status;
private String statusName; private String statusName;
@JsonFormatDttm @JsonFormatDttm private ZonedDateTime createdDttm;
private ZonedDateTime createdDttm; @JsonFormatDttm private ZonedDateTime updatedDttm;
@JsonFormatDttm @JsonFormatDttm private ZonedDateTime firstLoginDttm;
private ZonedDateTime updatedDttm; @JsonFormatDttm private ZonedDateTime lastLoginDttm;
@JsonFormatDttm
private ZonedDateTime firstLoginDttm;
@JsonFormatDttm
private ZonedDateTime lastLoginDttm;
public Basic( public Basic(
Long id, Long id,
UUID uuid, UUID uuid,
String userRole, String userRole,
String userRoleName, String userRoleName,
String name, String name,
String userId, String userId,
String employeeNo, String employeeNo,
String tempPassword, String tempPassword,
String status, String status,
String statusName, String statusName,
ZonedDateTime createdDttm, ZonedDateTime createdDttm,
ZonedDateTime updatedDttm, ZonedDateTime updatedDttm,
ZonedDateTime firstLoginDttm, ZonedDateTime firstLoginDttm,
ZonedDateTime lastLoginDttm ZonedDateTime lastLoginDttm) {
) {
this.id = id; this.id = id;
this.uuid = uuid; this.uuid = uuid;
this.userRole = userRole; this.userRole = userRole;
@@ -82,7 +77,6 @@ public class MembersDto {
StatusType type = EnumType.fromId(StatusType.class, status); StatusType type = EnumType.fromId(StatusType.class, status);
return type.getText(); return type.getText();
} }
} }
@Getter @Getter
@@ -91,7 +85,9 @@ public class MembersDto {
@AllArgsConstructor @AllArgsConstructor
public static class SearchReq { public static class SearchReq {
@Schema(description = "전체, 관리자(ROLE_ADMIN), 라벨러(ROLE_LABELER), 검수자(ROLE_REVIEWER)", example = "") @Schema(
description = "전체, 관리자(ROLE_ADMIN), 라벨러(ROLE_LABELER), 검수자(ROLE_REVIEWER)",
example = "")
private String userRole; private String userRole;
@Schema(description = "키워드", example = "홍길동") @Schema(description = "키워드", example = "홍길동")
@@ -135,7 +131,7 @@ public class MembersDto {
private String employeeNo; private String employeeNo;
public AddReq( public AddReq(
String userRole, String name, String userId, String tempPassword, String employeeNo) { String userRole, String name, String userId, String tempPassword, String employeeNo) {
this.userRole = userRole; this.userRole = userRole;
this.name = name; this.name = name;
this.userId = userId; this.userId = userId;

View File

@@ -36,7 +36,6 @@ public class AdminService {
membersCoreService.updateMembers(uuid, updateReq); membersCoreService.updateMembers(uuid, updateReq);
} }
/** /**
* 관리자 계정 미사용 처리 * 관리자 계정 미사용 처리
* *

View File

@@ -51,7 +51,7 @@ public class MembersService {
*/ */
private boolean isValidPassword(String password) { private boolean isValidPassword(String password) {
String passwordPattern = String passwordPattern =
"^(?=.*[A-Za-z])(?=.*\\d)(?=.*[!@#$%^&*()_+\\-\\[\\]{};':\"\\\\|,.<>/?]).{8,20}$"; "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[!@#$%^&*()_+\\-\\[\\]{};':\"\\\\|,.<>/?]).{8,20}$";
return Pattern.matches(passwordPattern, password); return Pattern.matches(passwordPattern, password);
} }
} }

View File

@@ -59,7 +59,7 @@ public class MembersCoreService {
*/ */
public void updateMembers(UUID uuid, MembersDto.UpdateReq updateReq) { public void updateMembers(UUID uuid, MembersDto.UpdateReq updateReq) {
MemberEntity memberEntity = MemberEntity memberEntity =
membersRepository.findByUUID(uuid).orElseThrow(MemberNotFoundException::new); membersRepository.findByUUID(uuid).orElseThrow(MemberNotFoundException::new);
if (StringUtils.isNotBlank(updateReq.getName())) { if (StringUtils.isNotBlank(updateReq.getName())) {
memberEntity.setName(updateReq.getName()); memberEntity.setName(updateReq.getName());
@@ -77,7 +77,6 @@ public class MembersCoreService {
membersRepository.save(memberEntity); membersRepository.save(memberEntity);
} }
/** /**
* 관리자 계정 미사용 처리 * 관리자 계정 미사용 처리
* *
@@ -85,14 +84,13 @@ public class MembersCoreService {
*/ */
public void deleteAccount(UUID uuid) { public void deleteAccount(UUID uuid) {
MemberEntity memberEntity = MemberEntity memberEntity =
membersRepository.findByUUID(uuid).orElseThrow(() -> new MemberNotFoundException()); membersRepository.findByUUID(uuid).orElseThrow(() -> new MemberNotFoundException());
memberEntity.setStatus("INACTIVE"); memberEntity.setStatus("INACTIVE");
memberEntity.setUpdatedDttm(ZonedDateTime.now()); memberEntity.setUpdatedDttm(ZonedDateTime.now());
membersRepository.save(memberEntity); membersRepository.save(memberEntity);
} }
/** /**
* 패스워드 변경 * 패스워드 변경
* *
@@ -100,10 +98,10 @@ public class MembersCoreService {
*/ */
public void resetPassword(String id, MembersDto.InitReq initReq) { public void resetPassword(String id, MembersDto.InitReq initReq) {
MemberEntity memberEntity = MemberEntity memberEntity =
membersRepository.findByUserId(id).orElseThrow(() -> new MemberNotFoundException()); membersRepository.findByUserId(id).orElseThrow(() -> new MemberNotFoundException());
String salt = String salt =
BCryptSaltGenerator.generateSaltWithEmployeeNo(memberEntity.getEmployeeNo().trim()); BCryptSaltGenerator.generateSaltWithEmployeeNo(memberEntity.getEmployeeNo().trim());
// 패스워드 암호화 // 패스워드 암호화
String hashedPassword = BCrypt.hashpw(initReq.getPassword(), salt); String hashedPassword = BCrypt.hashpw(initReq.getPassword(), salt);
@@ -112,6 +110,7 @@ public class MembersCoreService {
memberEntity.setUpdatedDttm(ZonedDateTime.now()); memberEntity.setUpdatedDttm(ZonedDateTime.now());
membersRepository.save(memberEntity); membersRepository.save(memberEntity);
} }
// //
/** /**
@@ -131,7 +130,10 @@ public class MembersCoreService {
* @return * @return
*/ */
public String getUserStatus(SignInRequest request) { public String getUserStatus(SignInRequest request) {
MemberEntity memberEntity = membersRepository.findByUserId(request.getUsername()).orElseThrow(MemberNotFoundException::new); MemberEntity memberEntity =
membersRepository
.findByUserId(request.getUsername())
.orElseThrow(MemberNotFoundException::new);
return memberEntity.getStatus(); return memberEntity.getStatus();
} }
} }

View File

@@ -61,8 +61,7 @@ public class CommonCodeRepositoryImpl implements CommonCodeRepositoryCustom {
.on(child.deleted.isFalse().or(child.deleted.isNull())) .on(child.deleted.isFalse().or(child.deleted.isNull()))
.where( .where(
commonCodeEntity.parent.isNull(), commonCodeEntity.parent.isNull(),
commonCodeEntity.deleted.isFalse().or(commonCodeEntity.deleted.isNull()) commonCodeEntity.deleted.isFalse().or(commonCodeEntity.deleted.isNull()))
)
.orderBy(commonCodeEntity.order.asc(), child.order.asc()) .orderBy(commonCodeEntity.order.asc(), child.order.asc())
.fetch(); .fetch();
} }

View File

@@ -33,11 +33,11 @@ public class MembersRepositoryImpl implements MembersRepositoryCustom {
@Override @Override
public boolean existsByUserId(String userId) { public boolean existsByUserId(String userId) {
return queryFactory return queryFactory
.selectOne() .selectOne()
.from(memberEntity) .from(memberEntity)
.where(memberEntity.userId.eq(userId)) .where(memberEntity.userId.eq(userId))
.fetchFirst() .fetchFirst()
!= null; != null;
} }
/** /**
@@ -49,7 +49,7 @@ public class MembersRepositoryImpl implements MembersRepositoryCustom {
@Override @Override
public Optional<MemberEntity> findByUserId(String userId) { public Optional<MemberEntity> findByUserId(String userId) {
return Optional.ofNullable( return Optional.ofNullable(
queryFactory.selectFrom(memberEntity).where(memberEntity.userId.eq(userId)).fetchOne()); queryFactory.selectFrom(memberEntity).where(memberEntity.userId.eq(userId)).fetchOne());
} }
/** /**
@@ -68,10 +68,11 @@ public class MembersRepositoryImpl implements MembersRepositoryCustom {
String contains = "%" + searchReq.getKeyword() + "%"; String contains = "%" + searchReq.getKeyword() + "%";
builder.and( builder.and(
memberEntity.name.likeIgnoreCase(contains) memberEntity
.or(memberEntity.userId.likeIgnoreCase(contains)) .name
.or(memberEntity.employeeNo.likeIgnoreCase(contains)) .likeIgnoreCase(contains)
); .or(memberEntity.userId.likeIgnoreCase(contains))
.or(memberEntity.employeeNo.likeIgnoreCase(contains)));
} }
// 권한 // 권한
@@ -80,35 +81,30 @@ public class MembersRepositoryImpl implements MembersRepositoryCustom {
} }
List<MembersDto.Basic> content = List<MembersDto.Basic> content =
queryFactory queryFactory
.select( .select(
Projections.constructor( Projections.constructor(
MembersDto.Basic.class, MembersDto.Basic.class,
memberEntity.id, memberEntity.id,
memberEntity.uuid, memberEntity.uuid,
memberEntity.userRole, memberEntity.userRole,
memberEntity.name, memberEntity.name,
memberEntity.userId, memberEntity.userId,
memberEntity.employeeNo, memberEntity.employeeNo,
memberEntity.tempPassword, memberEntity.tempPassword,
memberEntity.status, memberEntity.status,
memberEntity.createdDttm, memberEntity.createdDttm,
memberEntity.updatedDttm, memberEntity.updatedDttm,
memberEntity.firstLoginDttm, memberEntity.firstLoginDttm,
memberEntity.lastLoginDttm memberEntity.lastLoginDttm))
)) .from(memberEntity)
.from(memberEntity) .where(builder)
.where(builder) .offset(pageable.getOffset())
.offset(pageable.getOffset()) .limit(pageable.getPageSize())
.limit(pageable.getPageSize()) .orderBy(memberEntity.createdDttm.desc())
.orderBy(memberEntity.createdDttm.desc()) .fetch();
.fetch();
long total = long total = queryFactory.select(memberEntity).from(memberEntity).fetchCount();
queryFactory
.select(memberEntity)
.from(memberEntity)
.fetchCount();
return new PageImpl<>(content, pageable, total); return new PageImpl<>(content, pageable, total);
} }
@@ -122,6 +118,6 @@ public class MembersRepositoryImpl implements MembersRepositoryCustom {
@Override @Override
public Optional<MemberEntity> findByUUID(UUID uuid) { public Optional<MemberEntity> findByUUID(UUID uuid) {
return Optional.ofNullable( return Optional.ofNullable(
queryFactory.selectFrom(memberEntity).where(memberEntity.uuid.eq(uuid)).fetchOne()); queryFactory.selectFrom(memberEntity).where(memberEntity.uuid.eq(uuid)).fetchOne());
} }
} }