필터 체인(Filter Chain)
SpringSecurity architecture에 나와 있듯이 AuthenticationFilter는 요청과 가장 먼저 만나 AuthenticationManager에게 인증관리를 위임한다. 이러한 Filter들은 여러개가 존재할 수 있는데 그렇게 되면, 각각의 필터는 요청을 수신하여 최종적으로 다음 필터에게 요청을 전달하게 된다(인증 인가된 상황이라면) 이러한 여러 필터 구성을 필터 체인이라고 한다. 필터 체인은 상당한 개발 이점이 존재하는데 인증 시나리오에 맞혀서 구성이 가능하다는 것이다. (1차 인증, 2차 인증) 하나의 인증 흐름에 국한 하기 않고 분리할 수 있다는 것이 가장 큰 이점이다.
https://docs.spring.io/spring-security/reference/servlet/architecture.html
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().hasAuthority("read")
.requestMatchers("/hello1").hasRole("admin")
.requestMatchers("/hello2").hasRole("user")
)
//중략
);
return http.build();
}
이때까지 인증 인가 규칙을 지정할 수 있었던 Config의 filterChain이 바로 그것이였던 것!
HTTP filter
SpringSecurity는 다양한 filter를 제공하고 있다. 이중 HTTP filter이 대표적
필터들은 doFilter()를 메서드를 재정의하여 필터 논리를 구현해야한다. doFilter()는 다음과 같은 매개변수를 가짐
- ServletRequest() : Htttp 요청 세부정보
- ServletResponse() Http 응답 세부정보, 필터 체인에서 응답을 변경할 수 있음
- FilterChain() : 다음 필터로 요청을 전달
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
}
다음은 SpringSecurity에서 지정한 필터 구현 클래스
- BasicAuthenticationFilter: HTTP 인증 처리
- CsrfFilter: CSRF 처리
- CorsFilter CORS처리
필터는 앞과 뒤 또는 특정 위치를 지정하여 추가할 수 있는데 동일한 위치에 필터를 추가할 수 있음
앞에는 헤더 확인 로직, 뒤에는 로깅 과 같은 추가한다고 한다.
이렇게 동일한 위치에 여러 필터가 존재하는 경우 실행 순서가 보장되지 않음
필터 추가
헤더를 확인하는 필터를 추가해보자
public class RequestValidationFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String requestId=httpRequest.getHeader("Request-Id");
if(requestId == null || requestId.isBlank()){
httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}
chain.doFilter(request,response);
}
}
RequestValidationFilter를 추가하여 헤더에 "Request-Id" 값이 존재하는지 확인한다.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.addFilterBefore(
new RequestValidationFilter(),
AuthorizationFilter.class)
.authorizeHttpRequests((authz) -> authz
/*.anyRequest().hasAuthority("read")*/
.requestMatchers("/hello1").hasRole("admin")
.requestMatchers("/hello2").hasRole("user")
.anyRequest().hasAuthority("read")
)
.authenticationProvider(customAuthenticationProvider())
.httpBasic(c -> {
c.realmName("OTHER");
c.authenticationEntryPoint(new CustomEntryPoint());
})
.formLogin(forLogin -> forLogin
.successHandler(authenticationSuccessHandler())
.failureHandler(authenticationFailureHandler())
);
return http.build();
}
addFilterBefore()로 앞에 필터를 추가
구현도중 다른 문제가 있었는데, anyRequest를 Matcher앞에서 처리하니 Can't configure mvcMatchers after anyRequest애러가 발생했다. 아직 이유를 자세하게 모르지만 순서 문제같았다.
'보안' 카테고리의 다른 글
[Spring Security in action] OAuth 2 실습해보기 (0) | 2024.02.04 |
---|---|
[Spring Security in action] CSRF 보호 및 CORS 적용 (0) | 2024.02.03 |
[Spring Security in action] 권한부여 (0) | 2024.01.20 |
[Spring Security in action] SecurityContext (0) | 2024.01.18 |
[Spring Security in action] AuthenticationProvider을 사용한 인증 (0) | 2024.01.16 |