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]