Acegi Security는 여러 가지 필터를 사용하는데, 필터에 관해서는 본 참조 가이드의 나머지
부분에 걸쳐 설명할 것이다. 여러분은 이러한 필터들이 웹 애플리케이션에
추가되는 방법을 선택할 여지가 있는데, 여러분은
FilterToBeanProxy나 FilterChainProxy를
사용할 수 있다. 두 가지 모두 아래에서 알아볼 것이다.
대부분의 필터들은 FilterToBeanProxy를 이용하여 설정한다.
다음은 web.xml의 설정 예이다:
<filter>
<filter-name>Acegi HTTP Request Security Filter</filter-name>
<filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>org.acegisecurity.ClassThatImplementsFilter</param-value>
</init-param>
</filter>web.xml에 들어 있는 필터는 실제로는 FilterToBeanProxy이며 실질적으로
필터의 로직을 구현할 필터는 아니다. FilterToBeanProxy가 하는 일은
Spring 애플리케이션 컨텍스트로부터 획득한 빈에 필터의 메소드를
위임하는 것이다. 이렇게 하면 빈이 Spring 애플리케이션 컨텍스트 라이프 사이클 지원과
설정의 유연함으로부터 오는 이점을 얻을 수 있다.
빈은 javax.servlet.Filter를 구현해야 한다.
FilterToBeanProxy는 오직 하나의 초기화 매개변수만을 필요로 하는데,
초기화 매개변수는 targetClass나 targetBean이다.
targetBean이 빈의 이름으로 객체를 정하는 반면,
targetClass 매개변수는 애플리케이션 컨텍스트의 첫 번째 객체를
지정된 클래스에 위치시킨다. 보통의 Spring 웹 애플리케이션들과 같이
FilterToBeanProxy는
WebApplicationContextUtils.getWebApplicationContext(ServletContext)를 통해
애플리케이션 컨텍스트에 접근하므로 여러분은 web.xml에
ContextLoaderListener를 설정해야만 한다.
서블릿 컨테이너 대신 IoC 컨테이너상에서 Filter를
작동하게 하는 경우 고려해야할 라이프 사이클에 관련된 문제가 하나 있다.
구체적으로 말하자면 어느 컨테이너가 Filter의
"구동" 및"종료" 메소드를 호출할 책임이 있느냐이다. 필터의 초기화 및 소멸 순서는
서블릿 컨테이너마다 매우 다양할 수 있는데, 따라서 이렇게 될 경우 한
Filter가 앞서 초기화된 Filter에 의해
수립된 구성 설정에 의존하는 경우 문제가 발생할 수 있는 것으로 알려져 있다.
반면 Spring IoC 컨테이너는 잘 알려진 인터페이스 계약,
예상 가능한 메소드 호출 순서, 빈 자동묶기(autowiring) 지원, 그리고
Spring 인터페이스 구현을 생략할 수 있는 선택권(예, Spring XML의
destroy-method 메소드) 뿐만 아니라 좀 더 포괄적인
라이프 사이클/IoC 인터페이스(InitializingBean,
DisposableBean, BeanNameAware,
ApplicationContextAware 등과 같은)를 가진다.
이러한 연유로 가능하면 서블릿 컨테이너 라이프 사이클 서비스 보다는 Spring의
라이프 사이클 서비스를 사용할 것을 권장한다. 기본적으로
FilterToBeanProxy는 프록시화된 빈에
init(FilterConfig)과 destroy() 메소드를
위임하지 않을 것이다. 여러분이 이러한 메소드 호출이 위임되기를 원한다면
lifecycle 초기화 매개변수를
servlet-container-managed로 설정한다.
FilterToBeanProxy를 사용하기 보다는
FilterChainProxy를 사용하길 강력히 권장한다.
FilterToBeanProxy가 매우 유용한 클래스이긴 하지만
문제는 몇 가지 필터를 더 사용하게 될 경우 web.xml 파일 안의
<filter>와 <filter-mapping>에
입력해야할 코드 라인이 폭발적으로 증가한다는 점이다. 이러한 문제를 극복하기 위해
Acegi Security는 FilterChainProxy 클래스를 제공하고 있다.
FilterChainProxy는 FilterToBeanProxy를
이용하여 묶이기는 하나(바로 위 예제에서 처럼), 대상 클래스가
org.acegisecurity.util.FilterChainProxy이다.
필터 체인은 아래와 같은 코드를 이용하여 애플리케이션 컨텍스트내에 선언한다:
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/webServices/**=httpSessionContextIntegrationFilterWithASCFalse,basicProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor
/**=httpSessionContextIntegrationFilterWithASCTrue,authenticationProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor
</value>
</property>
</bean> 여러분이 FilterSecurityInterceptor를 선언하는 방식과
비슷하다는 것을 알아챌 지도 모르겠다. 둘 모두 정규 표현식과 Ant 스타일의 Path를
지원하며 가장 구체적인 URI가 먼저 나타난다. 런타임시에는
FilterChainProxy가 현재 요청되는 웹 요청과 일치하는
첫 번째 URL 패턴을 정할 것이다. 각 해당 설정 속성들은 애플리케이션
컨텍스트내에 정의되어 있는 빈의 이름을 나타낸다. 그런 다음 필터들은 정해진
FilterChain 행위에 따라 지정된 순서대로 호출될
것이다(Filter가 처리를 끝내고자 할 경우에는
체인에 대한 처리를 진행하지 않도록 결정할 수도 있다).
여러분도 알 수 있겠지만 FilterChainProxy는 서로 다른 요청 패턴에 대한
중복된 필터 이름을 필요로 한다(위 예제에서는 exceptionTranslationFilter와
filterSecurityInterceptor가 중복되어 있다).
설계상 이렇게 하도록 결정함으로써 FilterChainProxy는
서로 다른 URI 패턴에 대해 서로 다른 Filter 호출 순서를
지정할 수 있고 표현력을 향상시킬 수 있으며(정규 표현식, Ant 스타일의 Path,
커스텀 FilterInvocationDefinitionSource 구현 관점에서),
그리고 어떠한 Filter들이 호출되어야 하는지를 명확하게 할 수 있다.
여러분이 필터 체인 안에 두 개의 HttpSessionContextIntegrationFilter가
선언되어 있다는 것을 알고 있을지도 모르겠다(ASC는 allowSessionCreation의 줄임말이며,
HttpSessionContextIntegrationFilter의 프로퍼티이다). 웹 서비스는 차후에
이루어지는 요청에는 jsessionid를 제시하지 않을 것이므로 사용자 에이전트에 대한
HttpSession를 생성하는 것은 낭비일 수 있다. 만약 여러분이 최대의
확장성(scalability)를 요하는 대형(high-volume) 애플리케이션을 개발하고 있다면
위에 나타나 있는 접근법을 취할 것을 권장한다. 규모가 더 작은 애플리케이션에는
단일 HttpSessionContextIntegrationFilter
만으로도(기본 allowSessionCreation은
true로 지정) 충분할 것이다.
라이프 사이클 문제와 관련하여, FilterChainProxy에 대해
init(FilterConfig)와 destroy()
메소드가 호출되었다면, FilterChainProxy는
항상 그러한 메소드들을 기저의 Filter에 위임할 것이다.
이 경우 FilterChainProxy는
FilterInvocationDefinitionSource에 의해 몇 번이나 선언되었는지와는
관계없이 각 Filter에 대한 초기화와 종료가 한 번만
이루어 지도록 보장해 준다. 여러분은 FilterChainProxy를
프록시화하는 FilterToBeanProxy에 들어있는
lifecycle 초기화 매개변수를 통해 이러한 메소드들의
호출 여부에 대한 총괄적인 선택권을 제어한다. 위에서 논의한 바와 같이
기본적으로는 어떠한 서블릿 컨테이너 라이프사이클 호출도
FilterChainProxy로 위임되지는 않는다.
또한 여러분은 <URI Pattern> = <Filter Chain>
표현식 우측편에 #NONE# 토큰을 사용하여 필터 체인에서
URI 패턴을 생략할 수도 있다. 예를 들어 위 예제의 경우에는 여러분이
/webservices 위치를 완전히 생략하고자 한다면,
여러분은 빈 설정에서 해당 라인을 아래와 같이 변경할 수 있다:
/webServices/**=#NONE#
한 가지 알아둘 것은 이 패턴과 일치하는 것은 모두 아무런 인증이나 권한부여 서비스가 적용되지 않으므로 누구나 이곳에 자유로이 접근할 수 있을 것이라는 점이다.
web.xml에 필터가 정의되는 순서는 매우 중요하다.
실제로 여러분이 어떠한 필터를 사용하고 있는지와는 관계없이
<filter-mapping>의 순서는 아래와 같아야 한다:
ChannelProcessingFilter :
다른 프로토콜로 재지정(redirect)해야할 수도 있기 때문임
ConcurrentSessionFilter :
SecurityContextHolder의 기능을 사용하지는 않지만,
SessionRegistry를 갱신하여 현재 인증주체로부터 진행되고
있는 요청을 나타낼 필요가 있기 때문임
HttpSessionContextIntegrationFilter :
SecurityContext가 웹 요청 시작시
SecurityContextHolder내에 설정될 수 있으며,
웹 요청이 종료되는 경우(다음 웹 요청 사용을 준비) SecurityContext에
대한 모든 변경사항들이 HttpSession에 복사될 수 있음
인증 처리 메커니즘(Authentication processing mechanisms) :
AuthenticationProcessingFilter,
CasProcessingFilter,
BasicProcessingFilter, HttpRequestIntegrationFilter, JbossIntegrationFilter 등을
지정하여 SecurityContextHolder에
유효한 Authentication 요청 토큰이 포함되도록 변경할 수 있음.
SecurityContextHolderAwareRequestFilter :
여러분이 SecurityContextHolderAwareRequestFilter을 이용하여
서블릿 컨테이너에 Acegi Security를 인식하는
HttpServletRequestWrapper을 설치하는 경우
RememberMeProcessingFilter :
이전에 SecurityContextHolder을 갱신하는
인증 처리 메커니즘이 없었으며, 요청이 remember-me 서비스를 발생시키는
쿠키를 제시하여 적절히 기억된 Authentication 객체가
놓여지는 경우
AnonymousProcessingFilter :
이전에 SecurityContextHolder를 갱신하는
인증 처리 메커니즘이 없었으며, 익명 Authentication
객체가 놓여지는 경우
ExceptionTranslationFilter :
Acegi Security 예외를 잡아내어 HTTP 오류 응답이 반환되거나
적절한 AuthenticationEntryPoint가
실행될 수 있도록 하기 위함
FilterSecurityInterceptor : 웹 URI를 보호하기 위함
위의 필터들은 모두 FilterToBeanProxy이거나
FilterChainProxy이다.
각 애플리케이션에 대한 하나의 FilterChainProxy를
통해 하나의 FilterToBeanProxy만을 프록시화할 것을 권장하며,
그러한 FilterChainProxy가 모든 Acegi Security
Filter들을 정의하도록 한다.
만약 여러분이 SiteMesh를 사용하고 있다면 SiteMesh 필터가 호출되기 전에
Acegi Security 필터가 실행되도록 한다. 이렇게 하면 SiteMesh 데코레이터에
의해 사용되는 적절한 시점에 SecurityContextHolder에
정보들이 설정되도록 할 수 있다.