From 66b76252c2a57d3b73340204967b1e4f05fda80b Mon Sep 17 00:00:00 2001 From: Chanho Lee Date: Sun, 16 Feb 2025 22:51:08 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[PC-625]=20fix:=20=EC=95=A1=EC=84=B8?= =?UTF-8?q?=EC=8A=A4=20=ED=86=A0=ED=81=B0=20=EB=A7=8C=EB=A3=8C=EC=8B=9C=20?= =?UTF-8?q?401=20=EB=9C=A8=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/yapp/core/auth/jwt/JwtFilter.java | 68 +++++----- .../java/org/yapp/core/auth/jwt/JwtUtil.java | 123 +++++++++--------- 2 files changed, 94 insertions(+), 97 deletions(-) diff --git a/core/auth/src/main/java/org/yapp/core/auth/jwt/JwtFilter.java b/core/auth/src/main/java/org/yapp/core/auth/jwt/JwtFilter.java index 804946d0..46828ac8 100644 --- a/core/auth/src/main/java/org/yapp/core/auth/jwt/JwtFilter.java +++ b/core/auth/src/main/java/org/yapp/core/auth/jwt/JwtFilter.java @@ -20,49 +20,49 @@ @Component public class JwtFilter extends OncePerRequestFilter { - private final JwtUtil jwtUtil; + private final JwtUtil jwtUtil; - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, - FilterChain filterChain) - throws ServletException, IOException { + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) + throws ServletException, IOException { - String accessToken = request.getHeader("Authorization"); - if (accessToken == null || !accessToken.startsWith("Bearer ")) { - filterChain.doFilter(request, response); - return; - } - accessToken = accessToken.substring(7); + String accessToken = request.getHeader("Authorization"); + if (accessToken == null || !accessToken.startsWith("Bearer ")) { + filterChain.doFilter(request, response); + return; + } + accessToken = accessToken.substring(7); - try { - jwtUtil.isExpired(accessToken); - } catch (ExpiredJwtException e) { + try { + jwtUtil.isExpired(accessToken); + } catch (ExpiredJwtException e) { - PrintWriter writer = response.getWriter(); - writer.print("access token expired"); + PrintWriter writer = response.getWriter(); + writer.print("액세스 토큰이 만료되었습니다."); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - return; - } + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + return; + } - String category = jwtUtil.getCategory(accessToken); - if (!category.equals("access_token")) { - PrintWriter writer = response.getWriter(); - writer.print("invalid access token"); + String category = jwtUtil.getCategory(accessToken); + if (!category.equals("access_token")) { + PrintWriter writer = response.getWriter(); + writer.print("토큰의 카테고리가 액세스 토큰이 아닙니다."); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - return; - } + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + return; + } - Long userId = jwtUtil.getUserId(accessToken); - String role = jwtUtil.getRole(accessToken); + Long userId = jwtUtil.getUserId(accessToken); + String role = jwtUtil.getRole(accessToken); - Authentication authToken = - new UsernamePasswordAuthenticationToken(userId, null, Collections.singleton( - (GrantedAuthority) () -> role)); + Authentication authToken = + new UsernamePasswordAuthenticationToken(userId, null, Collections.singleton( + (GrantedAuthority) () -> role)); - SecurityContextHolder.getContext().setAuthentication(authToken); + SecurityContextHolder.getContext().setAuthentication(authToken); - filterChain.doFilter(request, response); - } + filterChain.doFilter(request, response); + } } diff --git a/core/auth/src/main/java/org/yapp/core/auth/jwt/JwtUtil.java b/core/auth/src/main/java/org/yapp/core/auth/jwt/JwtUtil.java index 51a8eae4..7aa20a51 100644 --- a/core/auth/src/main/java/org/yapp/core/auth/jwt/JwtUtil.java +++ b/core/auth/src/main/java/org/yapp/core/auth/jwt/JwtUtil.java @@ -11,79 +11,76 @@ @Component public class JwtUtil { - private final SecretKey secretKey; + private final SecretKey secretKey; - public JwtUtil(@Value("${spring.jwt.secret}") String secret) { - secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), - Jwts.SIG.HS256.key().build().getAlgorithm()); - } + public JwtUtil(@Value("${spring.jwt.secret}") String secret) { + secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), + Jwts.SIG.HS256.key().build().getAlgorithm()); + } - public String getOauthId(String token) { - String oauthId; - try { - oauthId = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token) - .getPayload().get("oauthId", String.class); - } catch (Exception e) { - throw new RuntimeException(); - } - return oauthId; + public String getOauthId(String token) { + String oauthId; + try { + oauthId = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token) + .getPayload().get("oauthId", String.class); + } catch (Exception e) { + throw new RuntimeException(); } + return oauthId; + } - public String getRole(String token) { - String role; - try { - role = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload() - .get("role", String.class); - } catch (Exception e) { - throw new RuntimeException(); - } - return role; + public String getRole(String token) { + String role; + try { + role = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload() + .get("role", String.class); + } catch (Exception e) { + throw new RuntimeException(); } + return role; + } - public Boolean isExpired(String token) { - boolean before; - try { - before = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token) - .getPayload().getExpiration().before(new Date()); - } catch (Exception e) { - throw new RuntimeException(); - } - return before; - } + public Boolean isExpired(String token) { + boolean before; - public String createJwt(String category, Long userId, String oauthId, String role, - Long expiredMs) { - return Jwts.builder() - .claim("category", category) - .claim("userId", userId) - .claim("oauthId", oauthId) - .claim("role", role) - .issuedAt(new Date(System.currentTimeMillis())) - .expiration(new Date(System.currentTimeMillis() + expiredMs)) - .signWith(secretKey) - .compact(); - } + before = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token) + .getPayload().getExpiration().before(new Date()); + return before; + } + + public String createJwt(String category, Long userId, String oauthId, String role, + Long expiredMs) { + return Jwts.builder() + .claim("category", category) + .claim("userId", userId) + .claim("oauthId", oauthId) + .claim("role", role) + .issuedAt(new Date(System.currentTimeMillis())) + .expiration(new Date(System.currentTimeMillis() + expiredMs)) + .signWith(secretKey) + .compact(); + } - public Long getUserId(String token) { - Long userId; - try { - userId = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token) - .getPayload().get("userId", Long.class); - } catch (Exception e) { - throw new RuntimeException(); - } - return userId; + public Long getUserId(String token) { + Long userId; + try { + userId = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token) + .getPayload().get("userId", Long.class); + } catch (Exception e) { + throw new RuntimeException(); } + return userId; + } - public String getCategory(String token) { - String category; - try { - category = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token) - .getPayload().get("category", String.class); - } catch (Exception e) { - throw new RuntimeException(); - } - return category; + public String getCategory(String token) { + String category; + try { + category = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token) + .getPayload().get("category", String.class); + } catch (Exception e) { + throw new RuntimeException(); } + return category; + } } From e2f4ea5b737c6e1abdd13b3738a6cc33fcfcd299 Mon Sep 17 00:00:00 2001 From: Chanho Lee Date: Sun, 16 Feb 2025 22:57:16 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[PC-625]=20feat:=20=EA=B6=8C=ED=95=9C=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/GlobalExceptionHandler.java | 137 +++++++++--------- 1 file changed, 72 insertions(+), 65 deletions(-) diff --git a/core/exception/src/main/java/org/yapp/core/exception/GlobalExceptionHandler.java b/core/exception/src/main/java/org/yapp/core/exception/GlobalExceptionHandler.java index 5fd6af06..9470706c 100644 --- a/core/exception/src/main/java/org/yapp/core/exception/GlobalExceptionHandler.java +++ b/core/exception/src/main/java/org/yapp/core/exception/GlobalExceptionHandler.java @@ -6,12 +6,14 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatusCode; import org.springframework.http.ResponseEntity; +import org.springframework.security.authorization.AuthorizationDeniedException; import org.springframework.validation.BindException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; +import org.yapp.core.exception.error.code.AuthErrorCode; import org.yapp.core.exception.error.code.CommonErrorCode; import org.yapp.core.exception.error.code.ErrorCode; import org.yapp.core.exception.error.response.ErrorResponse; @@ -21,79 +23,84 @@ @Slf4j public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { - @ExceptionHandler(ApplicationException.class) - public ResponseEntity handleQuizException(final ApplicationException e) { - final ErrorCode errorCode = e.getErrorCode(); - return handleExceptionInternal(errorCode); - } + @ExceptionHandler(ApplicationException.class) + public ResponseEntity handleQuizException(final ApplicationException e) { + final ErrorCode errorCode = e.getErrorCode(); + return handleExceptionInternal(errorCode); + } - @ExceptionHandler(IllegalArgumentException.class) - public ResponseEntity handleIllegalArgument(final IllegalArgumentException e) { - log.warn("handleIllegalArgument", e); - final ErrorCode errorCode = CommonErrorCode.INVALID_PARAMETER; - return handleExceptionInternal(errorCode, e.getMessage()); - } + @ExceptionHandler(AuthorizationDeniedException.class) + public ResponseEntity handleAuthorizationException(final AuthorizationDeniedException e) { + return handleExceptionInternal(AuthErrorCode.ACCESS_DENIED); + } - @Override - public ResponseEntity handleMethodArgumentNotValid( - final MethodArgumentNotValidException e, - final HttpHeaders headers, - final HttpStatusCode status, - final WebRequest request) { - log.warn("handleIllegalArgument", e); - final ErrorCode errorCode = CommonErrorCode.INVALID_PARAMETER; - return handleExceptionInternal(e, errorCode); - } + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity handleIllegalArgument(final IllegalArgumentException e) { + log.warn("handleIllegalArgument", e); + final ErrorCode errorCode = CommonErrorCode.INVALID_PARAMETER; + return handleExceptionInternal(errorCode, e.getMessage()); + } - @ExceptionHandler({Exception.class}) - public ResponseEntity handleAllException(final Exception ex) { - log.warn("handleAllException", ex); - final ErrorCode errorCode = CommonErrorCode.INTERNAL_SERVER_ERROR; - return handleExceptionInternal(errorCode); - } + @Override + public ResponseEntity handleMethodArgumentNotValid( + final MethodArgumentNotValidException e, + final HttpHeaders headers, + final HttpStatusCode status, + final WebRequest request) { + log.warn("handleIllegalArgument", e); + final ErrorCode errorCode = CommonErrorCode.INVALID_PARAMETER; + return handleExceptionInternal(e, errorCode); + } - private ResponseEntity handleExceptionInternal(final ErrorCode errorCode) { - return ResponseEntity.status(errorCode.getHttpStatus()) - .body(makeErrorResponse(errorCode)); - } + @ExceptionHandler({Exception.class}) + public ResponseEntity handleAllException(final Exception ex) { + log.warn("handleAllException", ex); + final ErrorCode errorCode = CommonErrorCode.INTERNAL_SERVER_ERROR; + return handleExceptionInternal(errorCode); + } - private ResponseEntity handleExceptionInternal(final ErrorCode errorCode, - final String message) { - return ResponseEntity.status(errorCode.getHttpStatus()) - .body(makeErrorResponse(errorCode, message)); - } + private ResponseEntity handleExceptionInternal(final ErrorCode errorCode) { + return ResponseEntity.status(errorCode.getHttpStatus()) + .body(makeErrorResponse(errorCode)); + } - private ResponseEntity handleExceptionInternal(final BindException e, - final ErrorCode errorCode) { - return ResponseEntity.status(errorCode.getHttpStatus()) - .body(makeErrorResponse(e, errorCode)); - } + private ResponseEntity handleExceptionInternal(final ErrorCode errorCode, + final String message) { + return ResponseEntity.status(errorCode.getHttpStatus()) + .body(makeErrorResponse(errorCode, message)); + } - private ErrorResponse makeErrorResponse(final ErrorCode errorCode) { - return ErrorResponse.builder() - .code(errorCode.name()) - .message(errorCode.getMessage()) - .build(); - } + private ResponseEntity handleExceptionInternal(final BindException e, + final ErrorCode errorCode) { + return ResponseEntity.status(errorCode.getHttpStatus()) + .body(makeErrorResponse(e, errorCode)); + } - private ErrorResponse makeErrorResponse(final ErrorCode errorCode, final String message) { - return ErrorResponse.builder() - .code(errorCode.name()) - .message(message) - .build(); - } + private ErrorResponse makeErrorResponse(final ErrorCode errorCode) { + return ErrorResponse.builder() + .code(errorCode.name()) + .message(errorCode.getMessage()) + .build(); + } - private ErrorResponse makeErrorResponse(final BindException e, final ErrorCode errorCode) { - final List validationErrorList = e.getBindingResult() - .getFieldErrors() - .stream() - .map(ErrorResponse.ValidationError::of) - .collect(Collectors.toList()); + private ErrorResponse makeErrorResponse(final ErrorCode errorCode, final String message) { + return ErrorResponse.builder() + .code(errorCode.name()) + .message(message) + .build(); + } - return ErrorResponse.builder() - .code(errorCode.name()) - .message(errorCode.getMessage()) - .errors(validationErrorList) - .build(); - } + private ErrorResponse makeErrorResponse(final BindException e, final ErrorCode errorCode) { + final List validationErrorList = e.getBindingResult() + .getFieldErrors() + .stream() + .map(ErrorResponse.ValidationError::of) + .collect(Collectors.toList()); + + return ErrorResponse.builder() + .code(errorCode.name()) + .message(errorCode.getMessage()) + .errors(validationErrorList) + .build(); + } } \ No newline at end of file From fe52345ce7816be4b7dd28d41b64911055dc4370 Mon Sep 17 00:00:00 2001 From: Chanho Lee Date: Sun, 16 Feb 2025 22:57:37 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[PC-625]=20feat:=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=8B=9C=ED=81=90=EB=A6=AC=ED=8B=B0=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yapp/global/config/SecurityConfig.java | 80 ++++++++++--------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/api/src/main/java/org/yapp/global/config/SecurityConfig.java b/api/src/main/java/org/yapp/global/config/SecurityConfig.java index 8c3a6413..07a1c5bd 100644 --- a/api/src/main/java/org/yapp/global/config/SecurityConfig.java +++ b/api/src/main/java/org/yapp/global/config/SecurityConfig.java @@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; @@ -20,51 +21,52 @@ @Configuration @EnableWebSecurity +@EnableMethodSecurity(securedEnabled = true, prePostEnabled = true) @RequiredArgsConstructor public class SecurityConfig { - private final JwtFilter jwtFilter; + private final JwtFilter jwtFilter; - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - return http.csrf(AbstractHttpConfigurer::disable) - .cors(corsConfigurer -> corsConfigurer.configurationSource(corsConfigurationSource())) - .httpBasic(AbstractHttpConfigurer::disable) - .sessionManagement( - configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .authorizeHttpRequests(registry -> registry - .requestMatchers(getMatcherForUserAndAdmin()) - .hasAnyRole("USER", "ADMIN") - .requestMatchers(getMatcherForAnyone()) - .permitAll() - .anyRequest() - .authenticated()) - .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) - .build(); - } + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + return http.csrf(AbstractHttpConfigurer::disable) + .cors(corsConfigurer -> corsConfigurer.configurationSource(corsConfigurationSource())) + .httpBasic(AbstractHttpConfigurer::disable) + .sessionManagement( + configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(registry -> registry + .requestMatchers(getMatcherForUserAndAdmin()) + .hasAnyRole("USER", "ADMIN") + .requestMatchers(getMatcherForAnyone()) + .permitAll() + .anyRequest() + .authenticated()) + .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) + .build(); + } - private CorsConfigurationSource corsConfigurationSource() { - return request -> { - CorsConfiguration config = new CorsConfiguration(); - config.setAllowedHeaders(Collections.singletonList("*")); - config.setAllowedMethods(Collections.singletonList("*")); - config.setAllowedOriginPatterns(Collections.singletonList("*")); - return config; - }; - } + private CorsConfigurationSource corsConfigurationSource() { + return request -> { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowedHeaders(Collections.singletonList("*")); + config.setAllowedMethods(Collections.singletonList("*")); + config.setAllowedOriginPatterns(Collections.singletonList("*")); + return config; + }; + } - private RequestMatcher getMatcherForAnyone() { - return RequestMatchers.anyOf(antMatcher("/api/login/**"), antMatcher("/api/**"), - antMatcher("/swagger-ui/**"), - antMatcher("/v3/api-docs/**"), antMatcher("/swagger-ui.html")); - } + private RequestMatcher getMatcherForAnyone() { + return RequestMatchers.anyOf(antMatcher("/api/login/**"), antMatcher("/api/**"), + antMatcher("/swagger-ui/**"), + antMatcher("/v3/api-docs/**"), antMatcher("/swagger-ui.html")); + } - private RequestMatcher getMatcherForRegister() { - return RequestMatchers.anyOf(antMatcher("/api/profiles/init")); - } + private RequestMatcher getMatcherForRegister() { + return RequestMatchers.anyOf(antMatcher("/api/profiles/init")); + } - private RequestMatcher getMatcherForUserAndAdmin() { - return RequestMatchers.anyOf(antMatcher("/user") //TODO: 임시이며 추후 url에 따라 수정해야. - ); - } + private RequestMatcher getMatcherForUserAndAdmin() { + return RequestMatchers.anyOf(antMatcher("/user") //TODO: 임시이며 추후 url에 따라 수정해야. + ); + } } \ No newline at end of file