[JWT 필터 및 보안 설정 개선] JwtTokenValidationFilter에서 불필요한 의존성을 제거하고, WebSecurity에서 공개 및 API 경로에 대한 SecurityFilterChain을 추가하여 인증 경로를 명확히 설정함. application.properties에서 허용 경로 설정을 제거하여 코드 정리.
This commit is contained in:
@@ -1,12 +1,10 @@
|
|||||||
package com.bio.bio_backend.global.filter;
|
package com.bio.bio_backend.global.filter;
|
||||||
|
|
||||||
import java.io.IOException;
|
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.domain.base.member.dto.MemberDto;
|
||||||
import com.bio.bio_backend.global.utils.HttpUtils;
|
import com.bio.bio_backend.global.utils.HttpUtils;
|
||||||
import org.springframework.core.env.Environment;
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
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.domain.base.member.service.MemberService;
|
||||||
import com.bio.bio_backend.global.constants.ApiResponseCode;
|
import com.bio.bio_backend.global.constants.ApiResponseCode;
|
||||||
import com.bio.bio_backend.global.utils.JwtUtils;
|
import com.bio.bio_backend.global.utils.JwtUtils;
|
||||||
import com.bio.bio_backend.global.config.SecurityPathConfig;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@@ -35,16 +33,8 @@ public class JwtTokenValidationFilter extends OncePerRequestFilter {
|
|||||||
private final JwtUtils jwtUtils;
|
private final JwtUtils jwtUtils;
|
||||||
private final HttpUtils httpUtils;
|
private final HttpUtils httpUtils;
|
||||||
private final MemberService memberService;
|
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
|
@Override
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import com.bio.bio_backend.global.filter.JwtTokenIssuanceFilter;
|
|||||||
import com.bio.bio_backend.global.filter.JwtTokenValidationFilter;
|
import com.bio.bio_backend.global.filter.JwtTokenValidationFilter;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
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.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
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.exception.CustomAuthenticationFailureHandler;
|
||||||
import com.bio.bio_backend.global.utils.JwtUtils;
|
import com.bio.bio_backend.global.utils.JwtUtils;
|
||||||
import com.bio.bio_backend.global.utils.HttpUtils;
|
import com.bio.bio_backend.global.utils.HttpUtils;
|
||||||
import com.bio.bio_backend.global.config.SecurityPathConfig;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@@ -35,12 +35,10 @@ public class WebSecurity {
|
|||||||
private final BCryptPasswordEncoder bCryptPasswordEncoder;
|
private final BCryptPasswordEncoder bCryptPasswordEncoder;
|
||||||
private final JwtUtils jwtUtils;
|
private final JwtUtils jwtUtils;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
private final Environment env;
|
|
||||||
private final SecurityPathConfig securityPathConfig;
|
|
||||||
private final HttpUtils httpUtils;
|
private final HttpUtils httpUtils;
|
||||||
private final MemberMapper memberMapper;
|
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);
|
JwtTokenIssuanceFilter filter = new JwtTokenIssuanceFilter(authenticationManager, jwtUtils, objectMapper, memberService, httpUtils, memberMapper);
|
||||||
filter.setFilterProcessesUrl("/login");
|
filter.setFilterProcessesUrl("/login");
|
||||||
filter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler(objectMapper));
|
filter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler(objectMapper));
|
||||||
@@ -48,14 +46,62 @@ public class WebSecurity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private JwtTokenValidationFilter getJwtTokenValidationFilter() {
|
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
|
@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 설정
|
// AuthenticationManager 설정
|
||||||
AuthenticationManagerBuilder authenticationManagerBuilder =
|
AuthenticationManagerBuilder authenticationManagerBuilder =
|
||||||
http.getSharedObject(AuthenticationManagerBuilder.class);
|
http.getSharedObject(AuthenticationManagerBuilder.class);
|
||||||
@@ -63,25 +109,16 @@ public class WebSecurity {
|
|||||||
|
|
||||||
AuthenticationManager authenticationManager = authenticationManagerBuilder.build();
|
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
|
http
|
||||||
// 1단계: JWT 토큰 발급 필터 (로그인 요청 처리 및 토큰 발급)
|
.csrf(AbstractHttpConfigurer::disable)
|
||||||
.addFilterBefore(getJwtTokenIssuanceFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class)
|
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
|
||||||
// 2단계: JWT 토큰 검증 필터 (자동 토큰 갱신 포함)
|
.authenticationManager(authenticationManager)
|
||||||
.addFilterBefore(getJwtTokenValidationFilter(), UsernamePasswordAuthenticationFilter.class);
|
.sessionManagement(session ->
|
||||||
|
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||||
|
)
|
||||||
|
.logout(AbstractHttpConfigurer::disable)
|
||||||
|
.addFilterBefore(getJwtTokenIssuanceFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class) // 토큰 발급
|
||||||
|
.addFilterBefore(getJwtTokenValidationFilter(), UsernamePasswordAuthenticationFilter.class); // 토큰 검증
|
||||||
|
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,10 +114,6 @@ springdoc.swagger-ui.disable-swagger-default-url=true
|
|||||||
springdoc.default-produces-media-type=application/json
|
springdoc.default-produces-media-type=application/json
|
||||||
springdoc.default-consumes-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
|
spring.servlet.multipart.enabled=true
|
||||||
|
|||||||
Reference in New Issue
Block a user