Spring Security를 공부하다가 문득 나온 Filter
얼핏 보았을때 뭔가를 거른다 ? 라는 생각이 들었다.
사실을 뭔가 ? 비슷하다
사실 Filter는 스프링만의 기능이 아닌 자바 서블릿에서도 제공하는 기능이지만
스프링 시큐리티에서 다양하게 활용된다.
Filter가 어디에 사용될까 ?
스프링 시큐리티에서 필터는
클라이언트가 서버에게 요청을 보내면 DispatcherServlet(FrontController) 로 요청이 전달되어 여러가지의 관문들을 통해
다시 클라이언트는 응답을 받게 되는데
Filter는 요청이 DispatcherServlet에 닿기전 , DispatcherServlet에서 응답이 나온후에 인증 ,권한 ,보안 처리를 하게된다.
쉽게 말해서 이상한 요청 거르고 필요한 정보 추가해서 보내고 받는 기능을 제공한다
Filter 의 종류
Spring을 하면서 아직도 헷갈리는 개념인데
같은 이름이여도 여러가지의 interface로 너무나도 많은 기능들이 존재한다.
SecurityContextPersistenceFilter -> 요청 전에 SecurityContextRepository에서 받아온 정보를 SecurityContextHolder에 넣는다.
LogoutFilter -> 로그아웃을 진행했는지 확인한다.
UsernamePasswordAuthenticationFilter -> login을 시도하게 되면 이 Filter를 거쳐간다
등 이 3가지 이외에도 여러가지의 필터들이 있다
궁금하면 찾아보자
하지만 이러한것들을 하나하나 다 구현할 필요는 없고
내가 사용해야는 기능을 단지 implement해서 사용하면 된다.
Filter의 사용방법
아래와 같이 만들어져있는 기능이 아닌 새롭게 내가 만들어서 사용한다면
가장 basic한 Filter을 구현하면 된다.
Filter의 doFilter를 오버라이딩해서 사용하는데
여기서 chain.doFilter(request , response) 를 꼭 입력해주어야 다음 필터로 넘어가는
필터 체인이 형성된다.
public class MyFilter1 implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("필터1 ");
chain.doFilter(request , response);
}
}
public class MyFilter2 implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("필터2 ");
chain.doFilter(request , response);
}
}
처음에는 설정 한다는게 참 이해가 안가고 어려웠지만
조금은 ? 익숙해진 @Configuration
FitlerConfig class를 생성해주고 필터에 대한 설정을 진행해준다
위에 선언되어있는 필터들을 @Bean으로 등록해주고 각각 FilterRegistrationBean<FilterName> 을 선언해준다.
addUrlPatterns : 반응할 Url을 선언해준다.
setOrder(number) -> 낮은 번호가 먼저 실행된다.
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<MyFilter1> filter1(){
FilterRegistrationBean<MyFilter1> bean = new FilterRegistrationBean<>(new MyFilter1());
bean.addUrlPatterns("/*");
bean.setOrder(1); // 낮은 번호가 필터중에서 가장 먼저 실행됨
return bean;
}
@Bean
public FilterRegistrationBean<MyFilter2> filter2(){
FilterRegistrationBean<MyFilter2> bean = new FilterRegistrationBean<>(new MyFilter2());
bean.addUrlPatterns("/*");
bean.setOrder(0); // 낮은 번호가 필터중에서 가장 먼저 실행됨
return bean;
}
}
당연하게도 SecurityConfig에서도 필터를 사용할수 있다.
하지만 필터체인보다 시큐리티 필터 체인이 먼저 사용된다는 점이 있다.
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http
// .addFilterBefore(new MyFilter2() , BasicAuthenticationFilter.class); 기본적으로 필터체인보다 시큐리티필터체인이 먼저 사용된다.
.csrf().disable().cors().disable()
.httpBasic(basic -> basic
.disable())
.sessionManagement(sessoin -> sessoin
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilter(new JwtAuthenticationFilter(authenticationManager))
.authorizeHttpRequests(request -> request
.requestMatchers("/api/v1/user/**").authenticated()
.requestMatchers("/api/v1/manager/**").hasAnyRole("MANAGER" , "ADMIN")
.requestMatchers("/api/v1/admin/**").hasAnyRole( "ADMIN")
.anyRequest().permitAll()
)
.formLogin().disable();
return http.build();
}
UsernamePasswordAuthenticationFilter 동작순서
위에서도 말한 UsernamePasswordAuthenticationFilter -> login을 시도하게 되면 이 Filter를 거쳐간다
동작순서는 다음과 같다
1.login을 username, password(Post)전송해서 요청을한다면
2.UsernamePasswordAuthenticationFilter가 실행되고 UsernamePasswordAuthenticationToken 객체가 생성되고
3.UsernamePasswordAuthenticationToken을 AuthenticationManager에게 전달해 실제로 사용자를 인증하도록한다.
4.이후 UserDetailsService를 구현한 클래스에서 loadUserByUsername() 메소드를 통해서 사용자 정보를 가져오고 비밀번호를 비교하여 인증을 시도한다.
5.만약 성공하면 SecurityContextHolder 에 인증된 사용자 정보 저장
성공시 -> AuthenticationSuccessHandler
실패시 -> AuthenticationFailureHandler 동작을 수행
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
System.out.println("로그인 시도 : jwtFilter");
return super.attemptAuthentication(request, response);
}
}
결론
필터를 등록하면 요청, 응답 하기전에 기능들을 넣을수 있다.
'Spring' 카테고리의 다른 글
[Spring][SpringSecurity] UserDetails,UserDetailsService 사용하는 이유 (0) | 2024.03.05 |
---|---|
[Spring][SpringSecurity] SecurityConfig 설정 (0) | 2024.03.04 |
[Spring] 벌크성 쿼리 주의점 (1) | 2024.01.13 |
[Spring] 스프링 데이터 JPA 페이징 (0) | 2024.01.12 |
[Spring] 스프링 JPA 메소드 설정 (0) | 2024.01.11 |