Jwt을 사용하여 사용자 인증을 하는 서비스를 만들면서 Filter를 만들어서 그 기능을 자동으로 실행되도록 하려고 했다.
이를 위해 SpringSecurity FilterChain에 JwtAuthenticationFilter
을 추가하였다.
SecurityConfig
코드는 아래와 같다.
@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig {
private final JwtProvider jwtProvider;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests() // 요청에 대한 사용권한 체크
.antMatchers("/auth/signup", "/auth/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().disable()
.logout().disable();
http.addFilterBefore(new JwtAuthenticationFilter(jwtProvider),
UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
JwtAuthenticationFilter
는 아래와 같다.
@Slf4j
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtProvider jwtProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
log.info("JwtAuthenticationFilter : jwt 인증 처리");
String jwtToken = parseJwt(request);
log.info("jwtToken = {}", jwtToken);
if (jwtToken != null) {
if (jwtProvider.validateToken(jwtToken)) {
Authentication auth = jwtProvider.getAuthentication(jwtToken);
SecurityContextHolder.getContext().setAuthentication(auth);
} else {
log.info("code : {}, message : {}", StatusCode.TOKEN_EXPIRE.getCode(),
StatusCode.TOKEN_EXPIRE.getMessage());
throw new TokenExpireException();
}
} else {
log.info("code : {}, message : {}", StatusCode.FORBIDDEN.getCode(),
StatusCode.FORBIDDEN.getMessage());
throw new ForbiddenException();
}
filterChain.doFilter(request, response);
}
private String parseJwt(HttpServletRequest request) {
String headerAuth = request.getHeader("Authorization");
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
return headerAuth.substring(7, headerAuth.length());
}
return null;
}
}
그러나 SecuritConfig에 특정 url은 모든 권한을 허용해주는 코드가 작동하지 않았다.
.antMatchers("/auth/signup", "/auth/login").permitAll()
내가 원했던 것은 이 url 은 Filter를 거치지 않는 것이였는데, 실제로는 Filter는 지나가고 권한은 허용해주는 것이였다.
그래서 다음 url이 Filter를 지나가지 않도록 하기 위해서는 OncePerRequestFilter
의 shouldNotFilter
함수를 Override해야 했다.
바뀐 JwtAuthenticationFilter는 아래와 같다.
Slf4j
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtProvider jwtProvider;
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
String[] excludePath = {"/auth/signup", "/auth/login"};
// 제외할 url 설정
String path = request.getRequestURI();
return Arrays.stream(excludePath).anyMatch(path::startsWith);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
log.info("JwtAuthenticationFilter : jwt 인증 처리");
String jwtToken = parseJwt(request);
log.info("jwtToken = {}", jwtToken);
if (jwtToken != null) {
if (jwtProvider.validateToken(jwtToken)) {
Authentication auth = jwtProvider.getAuthentication(jwtToken);
SecurityContextHolder.getContext().setAuthentication(auth);
} else {
log.info("code : {}, message : {}", StatusCode.TOKEN_EXPIRE.getCode(),
StatusCode.TOKEN_EXPIRE.getMessage());
throw new TokenExpireException();
}
} else {
log.info("code : {}, message : {}", StatusCode.FORBIDDEN.getCode(),
StatusCode.FORBIDDEN.getMessage());
throw new ForbiddenException();
}
filterChain.doFilter(request, response);
}
private String parseJwt(HttpServletRequest request) {
String headerAuth = request.getHeader("Authorization");
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
return headerAuth.substring(7, headerAuth.length());
}
return null;
}
}
shouldNotFilter
에서 설정한 excludePath
는 해당 Filter의 동작을 수행하지 않는 것을 확인했다.
다음은 authenticationEntryPoint
에 대해 정리하겠다.