Spring Security / / 2024. 10. 31. 10:14

FilterOrderRegistration 클래스 구조와 역할

이 클래스의 주요 역할은 Spring Security 필터들이 올바른 순서로 실행되도록 각 필터의 순서를 지정하는 것입니다.

@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;
}
 
}
 
}

 

1. FilterOrderRegistration 생성자

FilterOrderRegistration() {
    Step order = new Step(INITIAL_ORDER, ORDER_STEP);
    put(DisableEncodeUrlFilter.class, order.next());
    // 여러 필터들을 순차적으로 등록
    put(SwitchUserFilter.class, order.next());
}

 

이 생성자는 filterToOrder라는 맵에 다양한 Spring Security 필터들을 순서대로 등록합니다.

각 필터는 순서대로 고유한 순서 값(position)을 갖게 되며, 이 값은 100에서 시작해 각 필터가 100씩 증가하는 값을 갖습니다.

Step 객체인 order를 통해 next() 메서드를 호출하여 position 값을 순차적으로 증가시키며 필터를 등록합니다.

이를 통해 필터들이 filterToOrder 맵에 등록된 순서대로 실행될 수 있도록 보장합니다.

 

주요 필터와 그 역할

 

1. DisableEncodeUrlFilter

URL을 인코딩하지 않도록 설정하는 필터입니다. 보안 목적과 무관하며 주로 URL 처리 시의 특정 요구사항을 반영합니다.

2. ForceEagerSessionCreationFilter

세션을 필요로 하는 경우 세션을 즉시 생성하도록 강제하는 필터입니다.

3. ChannelProcessingFilter

요청이 HTTPS와 같은 특정 전송 채널을 통해서만 전달되도록 하는 필터입니다. 보안 채널을 강제하는 데 사용됩니다.

4. WebAsyncManagerIntegrationFilter

Spring의 비동기 요청 처리와 Spring Security를 통합해주는 필터입니다. 비동기 처리가 필요한 경우에도 보안을 유지할 수 있도록 합니다.

5. SecurityContextHolderFilter

SecurityContext 객체를 SecurityContextHolder에 설정하고 관리하는 필터입니다. 인증 정보를 전역적으로 접근 가능하게 유지합니다.

6. SecurityContextPersistenceFilter

SecurityContext를 세션에 저장하고 로드하는 역할을 합니다. 요청 사이에서 사용자 인증 상태를 유지하는 데 필요합니다.

7. HeaderWriterFilter

응답 헤더를 추가하거나 수정하는 필터입니다. 보안 헤더를 설정해 클릭재킹, XSS와 같은 공격을 방지하는 데 사용됩니다.

8. CorsFilter

Cross-Origin Resource Sharing (CORS) 정책을 적용하는 필터입니다. 다른 도메인에서의 요청을 허용할지 설정합니다.

9. CsrfFilter

CSRF(Cross-Site Request Forgery) 공격을 방지하기 위한 필터입니다. 요청의 유효성을 확인하는 CSRF 토큰을 검증합니다.

10. LogoutFilter

사용자가 로그아웃할 때 호출되는 필터입니다. 세션을 무효화하고 SecurityContext를 초기화하여 로그아웃을 처리합니다.

11. OAuth2AuthorizationRequestRedirectFilter

OAuth2 인증 요청을 위한 필터입니다. 사용자를 OAuth2 권한 서버로 리다이렉트하여 인증 요청을 시작합니다.

12. Saml2WebSsoAuthenticationRequestFilter

SAML2 프로토콜을 통해 SSO 인증 요청을 처리하는 필터입니다. SAML2 환경에서 SSO를 지원할 때 사용됩니다.

13. X509AuthenticationFilter

X.509 인증서를 사용하여 사용자 인증을 수행하는 필터입니다. SSL 클라이언트 인증이 필요한 경우 사용됩니다.

14. AbstractPreAuthenticatedProcessingFilter

사전 인증된 사용자의 인증 처리를 담당합니다. 예를 들어, SSO 등 외부 시스템에서 인증된 사용자를 처리할 때 사용됩니다.

15. CasAuthenticationFilter

중앙 인증 서비스(CAS)를 사용한 인증을 처리하는 필터입니다. 주로 교육기관 등에서 SSO 인증을 위해 사용됩니다.

16. OAuth2LoginAuthenticationFilter

OAuth2 로그인 요청을 처리하는 필터입니다. OAuth2AuthorizationRequestRedirectFilter를 통해 리다이렉트된 이후의 처리를 담당합니다.

17. Saml2WebSsoAuthenticationFilter

SAML2 프로토콜을 통해 SSO 로그인 요청을 처리합니다.

18. UsernamePasswordAuthenticationFilter

사용자 이름과 비밀번호를 통한 기본적인 인증을 처리하는 필터입니다. Spring Security의 가장 기본적인 인증 방식입니다.

19. DefaultLoginPageGeneratingFilter

기본 로그인 페이지를 생성하는 필터입니다. 로그인 페이지가 설정되지 않았을 때 기본 페이지를 제공합니다.

20. DefaultLogoutPageGeneratingFilter

기본 로그아웃 페이지를 생성하는 필터입니다. 로그아웃 페이지가 설정되지 않았을 때 기본 페이지를 제공합니다.

21. ConcurrentSessionFilter

동일한 계정으로 여러 세션을 제한하는 필터입니다. 특정 사용자가 하나의 세션만 유지하도록 할 때 사용됩니다.

22. DigestAuthenticationFilter

Digest 인증 방식을 처리하는 필터입니다. HTTP Basic 인증보다 안전한 인증 방법을 제공하는 데 사용됩니다.

23. BearerTokenAuthenticationFilter

JWT와 같은 Bearer 토큰 기반 인증을 처리하는 필터입니다.

24. BasicAuthenticationFilter

HTTP Basic 인증을 처리하는 필터입니다. 기본적인 인증 방식으로, 사용자 이름과 비밀번호를 이용합니다.

25. RequestCacheAwareFilter

요청 캐시를 관리하는 필터로, 인증이 필요한 요청을 캐시에 저장했다가 인증 후에 요청을 재시도하도록 합니다.

26. SecurityContextHolderAwareRequestFilter

SecurityContextHolder에 저장된 정보를 기반으로 요청에 보안 컨텍스트를 적용합니다.

27. JaasApiIntegrationFilter

JAAS(Java Authentication and Authorization Service)를 지원하는 필터입니다. JAAS API와의 통합을 지원합니다.

28. RememberMeAuthenticationFilter

Remember-Me 기능을 지원하는 필터입니다. 사용자가 로그인할 때 “Remember Me” 옵션을 선택하면, 다음 번 로그인 시 인증 상태를 유지합니다.

29. AnonymousAuthenticationFilter

인증되지 않은 사용자를 위한 기본 인증을 제공하는 필터입니다. 인증되지 않은 사용자에게 익명 권한을 부여합니다.

30. OAuth2AuthorizationCodeGrantFilter

OAuth2 코드 인증 방식을 처리하는 필터입니다. Authorization Code Grant 방식을 통해 인증을 수행합니다.

31. SessionManagementFilter

세션 관리와 관련된 설정을 적용하는 필터입니다. 예를 들어, 최대 세션 수 제한이나 세션 고정 공격 방지 설정을 처리합니다.

32. ExceptionTranslationFilter

인증 또는 권한 오류 발생 시 예외를 처리하는 필터입니다. 에러 페이지로 리다이렉트하거나 오류 메시지를 반환합니다.

33. FilterSecurityInterceptor

Spring Security의 접근 제어(ACL)를 적용하는 최종 필터입니다. 사용자 권한에 따라 요청이 허용되거나 거부됩니다.

34. AuthorizationFilter

요청에 대한 권한을 검사하는 필터입니다. 인증이 완료된 후, 사용자의 권한이 요청 리소스에 접근할 수 있는지 확인합니다.

35. SwitchUserFilter

관리자가 다른 사용자로 “스위치”하여 시스템에 접근할 수 있게 하는 필터입니다. 예를 들어, 관리자 계정이 사용자 계정으로 임시로 전환할 때 사용됩니다.

 

2. put(Class<? extends Filter> filter, int position)

void put(Class<? extends Filter> filter, int position) {
    this.filterToOrder.putIfAbsent(filter.getName(), position);
}

 

이 메서드는 특정 필터와 해당 필터의 실행 순서(position)를 filterToOrder 맵에 저장합니다.

putIfAbsent 메서드를 사용하여, 이미 필터가 등록되어 있다면 현재 position 값으로 덮어쓰지 않고 무시합니다. 이를 통해 필터의 중복 등록을 방지할 수 있습니다.

 

매개변수 설명:

 

filter: 등록할 필터의 클래스 타입 (Class<? extends Filter>)

position: 필터가 실행될 순서 (정수)

 

3. getOrder(Class<?> clazz)

Integer getOrder(Class<?> clazz) {
    while (clazz != null) {
        Integer result = this.filterToOrder.get(clazz.getName());
        if (result != null) {
            return result;
        }
        clazz = clazz.getSuperclass();
    }
    return null;
}

 

getOrder는 특정 필터 클래스(clazz)의 순서를 반환합니다.

filterToOrder 맵에서 해당 클래스 이름으로 순서를 찾고, 순서가 등록되지 않았다면 상위 클래스를 계속 탐색하여 등록된 순서 값을 찾습니다.

이는 필터가 상속 구조로 정의되었을 경우에도 올바르게 순서를 가져올 수 있도록 설계된 부분입니다.

 

반환 값:

 

필터가 등록된 순서 값(position)을 반환하며, 등록되지 않은 경우 null을 반환합니다.

 

사용 예시:

 

특정 필터의 실행 순서가 궁금할 때 getOrder(MyFilter.class)를 호출하여 순서를 확인할 수 있습니다.

 

4. Step 내부 클래스

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;
    }
}

 

Step 클래스는 필터의 순서 값(position)을 자동으로 증가시키는 기능을 제공합니다.

Step 객체는 초기 값(initialValue)과 증가 값(stepSize)을 받아서, next() 메서드를 호출할 때마다 현재 순서 값을 반환하고 다음 순서 값으로 증가시킵니다.

 

메서드 설명:

 

next(): 현재 순서 값(value)을 반환하고, stepSize만큼 증가시켜 다음 호출 시 반환할 순서 값을 설정합니다.

 

사용 예시:

 

Step order = new Step(100, 100);을 생성 후, order.next()를 호출할 때마다 100, 200, 300...의 순서를 생성합니다.

 

클래스의 전체 동작 요약

 

FilterOrderRegistration 클래스는 Spring Security에서 사용하는 다양한 필터를 순서에 따라 filterToOrder 맵에 저장합니다.

put() 메서드로 각 필터를 고유한 순서(position)로 등록하며, Step 클래스를 통해 순서를 순차적으로 생성합니다.

getOrder() 메서드는 특정 필터 클래스의 순서를 확인하고, 필요한 경우 상위 클래스를 통해 순서를 찾아줍니다.

이러한 순서 관리를 통해 Spring Security는 요청이 처리될 때 필터들이 올바른 순서대로 실행될 수 있도록 보장합니다.

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유