프로젝트에서 Spring Security + JWT + Redis 방식으로 인증/인가 로직을 구현하였고, 전체 과정을 기록으로 남겨두려 한다.
@Slf4j
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider jwtTokenProvider;
private final ObjectMapper objectMapper;
private final String UTF_8 = "utf-8";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
// 1. Request Header 로부터 Access Token을 추출한다.
String token = jwtTokenProvider.resolveToken((HttpServletRequest) request);
// 2. 추출한 Token의 유효성 검사를 진행한다.
if (token != null && jwtTokenProvider.validateToken(token)) {
// Token이 유효할 경우, Authentication 객체를 생성하여 SecurityContext에 저장한다.
Authentication authentication = jwtTokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
} catch (TokenException e) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setCharacterEncoding(UTF_8);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getWriter().write(
objectMapper.writeValueAsString(
ResponseDto.create(e.getMessage())
)
);
}
}
}
GenericFilterBean vs OncePerRequestFilter
키워드로 구글링해보면 된다!filterChain.doFilter()
메서드가 호출되며 그 다음 Filter로 넘어가게 된다.이 때 유의해야 할 점은 예외가 발생했을 경우 filterChain.doFilter()
메서드가 실행되지 않도록 try 문 내에 포함시켜야 한다는 점이다.
예외가 발생하면 이후의 Filter 들을 거치지 않고 바로 예외 상황에 맞는 응답을 하도록 설정하는 것이다.
(이렇게 안하니까 내가 설정한 예외 응답이 아닌 응답이 계속 떴다..ㅠ)
이후 인증/인가 관련 예외처리 과정
에서 자세히 다룬다.
👉 [Spring Security, JWT, Redis] 인증/인가 관련 예외 처리
// SecurityConfig
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtTokenProvider jwtTokenProvider;
private final ObjectMapper objectMapper;
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/~~~").permitAll()
.antMatchers("/~~~").authenticated()
.regexMatchers(HttpMethod.POST, "/~~~").authenticated()
.anyRequest().authenticated()
.and()
// addFilterBefore() 메서드를 통해 Filter를 등록할 수 있다.
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider, objectMapper),
UsernamePasswordAuthenticationFilter.class);;
return http.build();
}
}
addFilterBefore()
메서드를 통해 Filter를 등록할 수 있으며, UsernamePasswordAuthenticationFilter 앞에 JwtAuthenticationFilter 를 등록하는 것이다.