Spring Boot로 게시판 만들기(3)(filter)
로그인 인증을 위해 filter를 구현하게 되었다.
Filter
인터넷을 통해 찾아서 이해한 바로는,
Filter의 경우에는 디스패처 서블릿(Dispatcher Servlet)에 요청이 전달되기 전/후에 동작한다.
그에 반해 Interceptor는 디스패처 서블릿과 Controller사이에서 동작한다.
쉽게 이해할 수 있는 그림이 있기에 가져왔다.
보면 Filter는 Web Context에서 동작한다.
Spring Boot Security
Token을 이용한 로그인 기능을 구현하기 위해 수많은 작업이 필요하다.
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final TokenProvider tokenProvider;
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(WebSecurity web) {
web.ignoring()
.antMatchers("/h2-console/**", "/favicon.ico");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
// exception handling 할 때 우리가 만든 클래스를 추가
.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
.accessDeniedHandler(jwtAccessDeniedHandler)
.and()
.cors()
// h2-console 을 위한 설정을 추가
.and()
.headers()
.frameOptions()
.sameOrigin()
// 시큐리티는 기본적으로 세션을 사용
// 여기서는 세션을 사용하지 않기 때문에 세션 설정을 Stateless 로 설정
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// 로그인, 회원가입 API 는 토큰이 없는 상태에서 요청이 들어오기 때문에 permitAll 설정
.and()
.authorizeRequests()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated() // 나머지 API 는 전부 인증 필요
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/api/board", true)
// JwtFilter 를 addFilterBefore 로 등록했던 JwtSecurityConfig 클래스를 적용
.and()
.apply(new JwtSecurityConfig(tokenProvider));
}
}
public class JwtFilter extends OncePerRequestFilter {
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String BEARER_PREFIX = "Bearer ";
private final TokenProvider tokenProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
String jwt = resolveToken(request);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
Authentication authentication = tokenProvider.getAuthentication(jwt);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader(AUTHORIZATION_HEADER);
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(BEARER_PREFIX)) {
return bearerToken.substring(7);
}
return null;
}
}
이런식으로 configure 설정이 필요하다.
Filter의 경우 OncePerRequestFilter를 extends해주었는데
OncePerRequestFilter
이름에서도 알 수 있듯이 모든 서블릿에 일관된 요청을 처리하기 위해 만들어진 필터이다
사용자의 요청당 딱 한번 filter가 실행된다.