Spring Security ‐ Filter의 구성은 중요하다. - dnwls16071/Backend_Summary GitHub Wiki
📚 Spring Security의 필터 구성 순서
- SecurityFilterChain의 필터 순서는 매우 체계적이고 명확한 규칙에 따라 결정된다.
- 만약 순서가 뒤죽박죽이라면 인증되지 않은 사용자가 인가 필터를 먼저 거쳐 통과할 우려가 있거나 보안 컨텍스트(SecurityContext)가 만들어지기도 전에 다른 필터가 먼저 실행되어 의도치 않은 일이 발생할 수 있다.
- 이런 순서를 어떤 식으로 제어하고있는지를 잘 보여주는 클래스가 바로
FilterOrderRegistration이다.
@SuppressWarnings("serial")
final class FilterOrderRegistration {
private static final int INITIAL_ORDER = 100;
private static final int ORDER_STEP = 100;
private final Map<String, Integer> filterToOrder = new HashMap<>();
FilterOrderRegistration() {
Step order = new Step(INITIAL_ORDER, ORDER_STEP);
put(DisableEncodeUrlFilter.class, order.next());
put(ForceEagerSessionCreationFilter.class, order.next());
put(ChannelProcessingFilter.class, order.next());
order.next(); // gh-8105
put(WebAsyncManagerIntegrationFilter.class, order.next());
put(SecurityContextHolderFilter.class, order.next());
put(SecurityContextPersistenceFilter.class, order.next());
put(HeaderWriterFilter.class, order.next());
put(CorsFilter.class, order.next());
put(CsrfFilter.class, order.next());
put(LogoutFilter.class, order.next());
this.filterToOrder.put(
"org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter",
order.next());
this.filterToOrder.put(
"org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter",
order.next());
put(X509AuthenticationFilter.class, order.next());
put(AbstractPreAuthenticatedProcessingFilter.class, order.next());
this.filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter", order.next());
this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter",
order.next());
this.filterToOrder.put(
"org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter",
order.next());
put(UsernamePasswordAuthenticationFilter.class, order.next());
order.next(); // gh-8105
put(DefaultLoginPageGeneratingFilter.class, order.next());
put(DefaultLogoutPageGeneratingFilter.class, order.next());
put(ConcurrentSessionFilter.class, order.next());
put(DigestAuthenticationFilter.class, order.next());
this.filterToOrder.put(
"org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter",
order.next());
put(BasicAuthenticationFilter.class, order.next());
put(RequestCacheAwareFilter.class, order.next());
put(SecurityContextHolderAwareRequestFilter.class, order.next());
put(JaasApiIntegrationFilter.class, order.next());
put(RememberMeAuthenticationFilter.class, order.next());
put(AnonymousAuthenticationFilter.class, order.next());
this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter",
order.next());
put(SessionManagementFilter.class, order.next());
put(ExceptionTranslationFilter.class, order.next());
put(FilterSecurityInterceptor.class, order.next());
put(AuthorizationFilter.class, order.next());
put(SwitchUserFilter.class, order.next());
}
/**
* Register a {@link Filter} with its specific position. If the {@link Filter} was
* already registered before, the position previously defined is not going to be
* overriden
* @param filter the {@link Filter} to register
* @param position the position to associate with the {@link Filter}
*/
void put(Class<? extends Filter> filter, int position) {
this.filterToOrder.putIfAbsent(filter.getName(), position);
}
/**
* Gets the order of a particular {@link Filter} class taking into consideration
* superclasses.
* @param clazz the {@link Filter} class to determine the sort order
* @return the sort order or null if not defined
*/
Integer getOrder(Class<?> clazz) {
while (clazz != null) {
Integer result = this.filterToOrder.get(clazz.getName());
if (result != null) {
return result;
}
clazz = clazz.getSuperclass();
}
return null;
}
private static class Step {
private int value;
private final int stepSize;
Step(int initialValue, int stepSize) {
this.value = initialValue;
this.stepSize = stepSize;
}
int next() {
int value = this.value;
this.value += this.stepSize;
return value;
}
}
}
📚 Spring Security => 결론적으로 Filter의 집합체이다.
- 위의 코드에서 보았듯이 Spring Security의 인증/인가 처리 방식은 각각의 필터들에 의해서 처리된다.
- 각 필터가 "무엇에 포커스를 맞춰 처리하는가"에 대해서 간략하게 정리해보자.
[DisableEncodeUrlFilter]
[ForceEagerSessionCreationFilter]
[ChannelProcessingFilter]
[WebAsyncManagerIntegrationFilter]
[SecurityContextHolderFilter]
[SecurityContextPersistenceFilter]
[HeaderWriterFilter]
[CorsFilter]
[CsrfFilter]
[LogoutFilter]
[OAuth2AuthorizationRequestRedirectFilter]
[Saml2WebSsoAuthenticationRequestFilter]
[X509AuthenticationFilter]
[AbstractPreAuthenticatedProcessingFilter]
[CasAuthenticationFilter]
[OAuth2LoginAuthenticationFilter]
[Saml2WebSsoAuthenticationFilter]
[UsernamePasswordAuthenticationFilter]
[DefaultLoginPageGeneratingFilter]
[DefaultLogoutPageGeneratingFilter]
[ConcurrentSessionFilter]
[DigestAuthenticationFilter]
[BearerTokenAuthenticationFilter]
[BasicAuthenticationFilter]
[RequestCacheAwareFilter]
[SecurityContextHolderAwareRequestFilter]
[JaasApiIntegrationFilter]
[RememberMeAuthenticationFilter]
[AnonymousAuthenticationFilter]
[OAuth2AuthorizationCodeGrantFilter]
[SessionManagementFilter]
[ExceptionTranslationFilter]
[FilterSecurityInterceptor]
[AuthorizationFilter]
[SwitchUserFilter]