From cd689211ec49e2202e893c9c3bfdd0db59c16c87 Mon Sep 17 00:00:00 2001 From: sohot8653 Date: Fri, 5 Sep 2025 16:38:25 +0900 Subject: [PATCH] =?UTF-8?q?[JWT=20=ED=95=84=ED=84=B0=20=EB=B0=8F=20?= =?UTF-8?q?=EB=B3=B4=EC=95=88=20=EC=84=A4=EC=A0=95=20=EA=B0=9C=EC=84=A0]?= =?UTF-8?q?=20JwtTokenValidationFilter=EC=97=90=EC=84=9C=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EC=9D=98=EC=A1=B4=EC=84=B1?= =?UTF-8?q?=EC=9D=84=20=EC=A0=9C=EA=B1=B0=ED=95=98=EA=B3=A0,=20WebSecurity?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EA=B3=B5=EA=B0=9C=20=EB=B0=8F=20API=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=EC=97=90=20=EB=8C=80=ED=95=9C=20SecurityFilt?= =?UTF-8?q?erChain=EC=9D=84=20=EC=B6=94=EA=B0=80=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EA=B2=BD=EB=A1=9C=EB=A5=BC=20=EB=AA=85?= =?UTF-8?q?=ED=99=95=ED=9E=88=20=EC=84=A4=EC=A0=95=ED=95=A8.=20application?= =?UTF-8?q?.properties=EC=97=90=EC=84=9C=20=ED=97=88=EC=9A=A9=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EC=84=A4=EC=A0=95=EC=9D=84=20=EC=A0=9C=EA=B1=B0?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=EC=BD=94=EB=93=9C=20=EC=A0=95=EB=A6=AC.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../filter/JwtTokenValidationFilter.java | 16 +--- .../global/security/WebSecurity.java | 89 +++++++++++++------ src/main/resources/application.properties | 4 - 3 files changed, 66 insertions(+), 43 deletions(-) diff --git a/src/main/java/com/bio/bio_backend/global/filter/JwtTokenValidationFilter.java b/src/main/java/com/bio/bio_backend/global/filter/JwtTokenValidationFilter.java index f7b2386..5a163ca 100644 --- a/src/main/java/com/bio/bio_backend/global/filter/JwtTokenValidationFilter.java +++ b/src/main/java/com/bio/bio_backend/global/filter/JwtTokenValidationFilter.java @@ -1,12 +1,10 @@ package com.bio.bio_backend.global.filter; import java.io.IOException; -import java.time.LocalDateTime; -import java.util.Objects; import com.bio.bio_backend.domain.base.member.dto.MemberDto; import com.bio.bio_backend.global.utils.HttpUtils; -import org.springframework.core.env.Environment; + import org.springframework.http.MediaType; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; @@ -24,7 +22,7 @@ import com.bio.bio_backend.global.dto.ApiResponseDto; import com.bio.bio_backend.domain.base.member.service.MemberService; import com.bio.bio_backend.global.constants.ApiResponseCode; import com.bio.bio_backend.global.utils.JwtUtils; -import com.bio.bio_backend.global.config.SecurityPathConfig; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -35,16 +33,8 @@ public class JwtTokenValidationFilter extends OncePerRequestFilter { private final JwtUtils jwtUtils; private final HttpUtils httpUtils; private final MemberService memberService; - private final Environment env; - private final SecurityPathConfig securityPathConfig; - @Override - protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { - String path = request.getRequestURI(); - String contextPath = env.getProperty("server.servlet.context-path", ""); - - return securityPathConfig.isPermittedPath(path, contextPath); - } + @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, diff --git a/src/main/java/com/bio/bio_backend/global/security/WebSecurity.java b/src/main/java/com/bio/bio_backend/global/security/WebSecurity.java index b209f47..ae8e7c4 100644 --- a/src/main/java/com/bio/bio_backend/global/security/WebSecurity.java +++ b/src/main/java/com/bio/bio_backend/global/security/WebSecurity.java @@ -4,7 +4,8 @@ import com.bio.bio_backend.global.filter.JwtTokenIssuanceFilter; import com.bio.bio_backend.global.filter.JwtTokenValidationFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; +import org.springframework.core.annotation.Order; + import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -23,7 +24,6 @@ import com.bio.bio_backend.domain.base.member.mapper.MemberMapper; import com.bio.bio_backend.global.exception.CustomAuthenticationFailureHandler; import com.bio.bio_backend.global.utils.JwtUtils; import com.bio.bio_backend.global.utils.HttpUtils; -import com.bio.bio_backend.global.config.SecurityPathConfig; import lombok.RequiredArgsConstructor; @Configuration @@ -35,12 +35,10 @@ public class WebSecurity { private final BCryptPasswordEncoder bCryptPasswordEncoder; private final JwtUtils jwtUtils; private final ObjectMapper objectMapper; - private final Environment env; - private final SecurityPathConfig securityPathConfig; private final HttpUtils httpUtils; private final MemberMapper memberMapper; - private JwtTokenIssuanceFilter getJwtTokenIssuanceFilter(AuthenticationManager authenticationManager) throws Exception { + private JwtTokenIssuanceFilter getJwtTokenIssuanceFilter(AuthenticationManager authenticationManager) { JwtTokenIssuanceFilter filter = new JwtTokenIssuanceFilter(authenticationManager, jwtUtils, objectMapper, memberService, httpUtils, memberMapper); filter.setFilterProcessesUrl("/login"); filter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler(objectMapper)); @@ -48,14 +46,62 @@ public class WebSecurity { } private JwtTokenValidationFilter getJwtTokenValidationFilter() { - return new JwtTokenValidationFilter(jwtUtils, httpUtils, memberService, env, securityPathConfig); + return new JwtTokenValidationFilter(jwtUtils, httpUtils, memberService); } - + /** + * 공개 경로용 SecurityFilterChain (우선순위 1) + * + * 처리 경로: + * - 인증 관련: /login, /logout, /members/register + * - API 문서: /swagger-ui/**, /api-docs/** + * - WebSocket: /ws/** + * - 모니터링: /actuator/** + * + * 모든 경로는 인증 없이 접근 가능 + */ @Bean - SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + @Order(1) + SecurityFilterChain publicChain(HttpSecurity http) throws Exception { + http + .securityMatcher( + // 인증 관련 공개 경로 + "/logout", "/members/register", + + // Swagger UI 관련 경로 + "/swagger-ui/**", "/swagger-ui.html", "/swagger-ui/index.html", + + // API 문서 관련 경로 + "/api-docs", "/api-docs/**", "/v3/api-docs", "/v3/api-docs/**", + + // WebSocket 관련 경로 + "/ws/**", + + // Actuator 모니터링 경로 + "/actuator/**", "/actuator/health/**", "/actuator/info" + ) + .csrf(AbstractHttpConfigurer::disable) + .authorizeHttpRequests(auth -> auth.anyRequest().permitAll()) + .sessionManagement(session -> + session.sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ); + return http.build(); + } + /** + * API 경로용 SecurityFilterChain (우선순위 2) + * + * 처리 경로: publicChain에서 처리되지 않는 모든 경로 + * - 비즈니스 API 엔드포인트 + * - 사용자 데이터 관련 API + * - 관리자 기능 API + * + * 모든 요청에 대해 JWT 토큰 인증 필수 + */ + @Bean + @Order(2) + SecurityFilterChain apiChain(HttpSecurity http) throws Exception { // AuthenticationManager 설정 AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class); @@ -63,25 +109,16 @@ public class WebSecurity { AuthenticationManager authenticationManager = authenticationManagerBuilder.build(); - // 설정 파일에서 허용할 경로 가져오기 - String[] permitAllPaths = securityPathConfig.getPermitAllPaths().toArray(new String[0]); - - http.csrf(AbstractHttpConfigurer::disable) //csrf 비활성화 - .authorizeHttpRequests(request -> //request 허용 설정 - request - .requestMatchers(permitAllPaths).permitAll() // 설정 파일에서 허용할 경로 - .anyRequest().authenticated() // 나머지 요청은 인증 필요 - ) - .authenticationManager(authenticationManager) - .sessionManagement(session -> - session.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 세션 사용 안함 - ) - .logout(AbstractHttpConfigurer::disable); http - // 1단계: JWT 토큰 발급 필터 (로그인 요청 처리 및 토큰 발급) - .addFilterBefore(getJwtTokenIssuanceFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class) - // 2단계: JWT 토큰 검증 필터 (자동 토큰 갱신 포함) - .addFilterBefore(getJwtTokenValidationFilter(), UsernamePasswordAuthenticationFilter.class); + .csrf(AbstractHttpConfigurer::disable) + .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) + .authenticationManager(authenticationManager) + .sessionManagement(session -> + session.sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ) + .logout(AbstractHttpConfigurer::disable) + .addFilterBefore(getJwtTokenIssuanceFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class) // 토큰 발급 + .addFilterBefore(getJwtTokenValidationFilter(), UsernamePasswordAuthenticationFilter.class); // 토큰 검증 return http.build(); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index f888bc0..ccdff03 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -114,10 +114,6 @@ springdoc.swagger-ui.disable-swagger-default-url=true springdoc.default-produces-media-type=application/json springdoc.default-consumes-media-type=application/json -# ======================================== -# 보안 설정 - 허용할 경로 -security.permit-all-paths=/login,/logout,/members/register,/swagger-ui/**,/swagger-ui.html,/swagger-ui/index.html,/api-docs,/api-docs/**,/v3/api-docs,/v3/api-docs/**,/ws/**,/actuator/**,/actuator/health/**,/actuator/info - # 파일 업로드 설정 # ======================================== spring.servlet.multipart.enabled=true