原创作者: leondu 阅读:391次 评论:0条 更新时间:2006-12-31 收藏
第三章. 协助系统
本章介绍一些Acegi Security使用的附加和协助系统。那些和安全无关,但是包含在Acegi Security项目中的部分,将会在本章中讨论
3.1. 本地化
Acegi Security支持对终端客户可能会看到的异常信息进行本地化。如果你的应用是为英文用户设计的,那么你什么都不用做,因为Acegi Security的所有消息默认都是英文的。如果你要支持其他区域用户,那么本节包含了你所需要了解的所有东西。
包括认证失败或者访问被拒绝(授权失败)的所有异常消息都可以被本地化。提供给开发者或者系统部署人员的异常或者日志信息(包括错误的属性、接口不符、构造器错误、debug级日志)没有被本地化,它们硬编码在Acegi Security的代码中。
在acegi-security-xx.jar(译注:xx代表版本号)的org.acegisecurity包中包含了一个 messages.properties文件。这个文件会被你的application context引用,因为Acegi Security实现了Spring的MessageSourceAware接口,它期待在application context启动的时候注入一个message resolver。通常你所需要做的是在你的application context中注册一个引用这个消息的bean,如下所示:
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename">
<value>org/acegisecurity/messages</value>
</property>
</bean>
messages.properties是按照资源包标准命名的,它代表了Acegi Securtiy支持的默认语言。文件默认是英文的。如果你不注册一个消息源,Acegi Security仍然可以正常工作,它会用回硬编码的英文消息。
如果你想定制messages.properties文件,或者支持其他语言,那么你应该copy这个文件,然后重命名,并在上述的bean定义中 注册。因为文件中的key并不多,因此本地化花不了多少工夫。如果你针对消息文件进行了本地化,那么请和社区分享,你可以添加一个JIRA任务,将你正确 命名的messages.properties本地化文件作为附件添加。
为了完善关于本地化的讨论需要知道Spring的ThreadLocal org.springframework.context.i18n.LocaleContextHolder。你应该为每个用户设置代表他区域的 LocaleContextHolder。Acegi Security会尝试从这个ThreadLocal中获取的Locale来从消息源中获取消息。请参考Spring的文档以获取更多使用 LocaleContextHolder和能够帮你自动设置它的辅助类(例如
AcceptHeaderLocaleResolver, CookieLocaleResolver, FixedLocaleResolver, SessionLocaleResolver 等)的详细信息。
3.2. Filters
正如你在整个手册中看到的那样,Acegi Security使用很多filter。你可以使用FilterToBeanProxy或者FilterChainProxy来确定这些是怎样加入到你的web应用中的,下面我们来看看。
大部分filter使用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中的filter实际上是一个FilterToBeanProxy,而不是真正实现filter逻辑的filter。 FilterToBeanProxy所作的是代理Filter的方法到一个从Spring的application context 获取的bean。这使得这个bean可以享受Spring application context的生命周期支持以及配置灵活性。这个bean必须实现javax.servlet.Filter。
FilterToBeanProxy只需要一个简单的初始化参数,targetClass或者targetBean。targetClass会定位 application context中指定的类的第一个对象,而FilterToBeanProxy按照bean的名字定位对象。象标准的Spring web应用一样,FilterToBeanProxy使用 WebApplicationContextUtils.getWebApplicationContext(ServletContext)来访问 application context,所以你应该在web.xml中配置一个ContextLoaderListener。
在IoC容器而不是servlet容器中部署Filter会有一个生命周期的问题。特别是,哪个容器应该负责调用Filter的"startup" 和 "shutdown"方法?注意到Filter的初始化和析构顺序随servlet容器不同而不同,如果一个Filter依赖于由另一个更早初始化的 Filter的配置,这样就会出现问题。另一方面,Spring IoC具备更加完善的生命周期/IoC接口(例如InitializingBean, DisposableBean, BeanNameAware, ApplicationContextAware以及其他许多)以及一个容易理解的接口契约(interface contract),可预见的方法调用顺序,自动装配支持,以及可以避免实现Spring接口的选项(例如Spring XML中的destroy-method 属性)。因此,我们推荐尽可能使用Spring生命周期服务而不是servlet容器生命周期服务。FilterToBeanProxy默认不会将 init(FilterConfig) 和 destroy()方法委派到被代理的bean。如果你需要这些调用被委派,那么将lifecycle初始化参数设置为servlet- container-managed。
我们强烈推荐你使用FilterChainProxy而不是FilterToBeanProxy。虽然FilterToBeanProxy是一个非 常有用的类FilterToBeanProxy,问题是当web.xml中filter变多时, 和 项就会太多而变得臃肿不堪。为了解决这个问题,Acegi Security提供一个FilterChainProxy类。它在FilterToBeanProxy中被装配(正如上面例子中所示),但目标类 (target class)是org.acegisecurity.util.FilterChainProxy。这样过滤器链(filter chain)可以在application context中按照如下代码配置:
<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,
/*=httpSessionContextIntegrationFilterWithASCTrue,authenticationProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor
</value>
</property>
</bean>
你可能注意到FilterSecurityInterceptor定义方式的相似之处。同时支持正则表达式和Ant Paths格式,越对应的URI越早出现。在运行时,FilterChainProxy会定位符合当前的web请求的第一个URI模式。每个对应的配置属 性代表了在application context中定义的一个bean的名字。接着fiter会按照它们被指定的顺序,按照FilterChain的标准行为模式被调用(如果一个 Filter决定停止处理,它可以不在chain中执行)。
如你所见,FilterChainProxy需要为不同的请求模式重复配置filter的名字(在上面的例子中,, exceptionTranslationFilter 和 filterSecurityInterceptor 是重复的)。这样的设计是为了让FilterChainProxy能够为不同的URI配置不同的filter调用顺序,同时也提高了表达力(针对正则表达 式、Ant Paths、以及任何FilterInvocationDefinitionSource的特定实现)和清晰度,可以知道是哪个filter应该被调用。
你可能注意到了我们在filter chain定义了两个HttpSessionContextIntegrationFilter (ASC是allowSessionCreation的缩写,是HttpSessionContextIntegrationFilter的一个属性)。 因为web服务不会为将来的请求提供一个jsessionid,为这样的用户创建HttpSessions是浪费的。如果你有一个需要最大限度的伸缩性的 高容量的应用,我们建议你使用上述的方法。对于小的应用,使用单一的HttpSessionContextIntegrationFilter (默认的allowSessionCreation设为true)应该足够了。
说到生命周期问题,如果对FilterChainProxy自身调用init(FilterConfig) 和 destroy()方法,它会把它代理到底层的filter。这样FilterChainProxy保证只初始化和析构每个filter一次,不论它在 FilterInvocationDefinitionSource中定义了多少次。你可以通过FilterToBeanProxy的lifecycle 初始化参数来控制这些方法是否被调用。如上面所讨论的那样,默认所有servlet容器生命周期调用是不被代理到FilterChainProxy的。
在web.xml中定义的filter的顺序是非常重要的。不管你实际用到哪个filter,的顺序应该是如下所示的:
1.ChannelProcessingFilter,因为可能要重定向到另一种协议。
2.ConcurrentSessionFilter 因为不使用任何SecurityContextHolder的功能,但是需要更新SessionRegistry来表示当前的发送请求的principal。
3. HttpSessionContextIntegrationFilter, 这样当一个web请求开始的时候就可以在SecurityContextHolder中设置一个SecurityContext,当web请求结束的时候 任何对SecurityContext的改动都会被copy到HttpSession(以备下一个web请求使用)。
4.Authentication processing mechanisms - AuthenticationProcessingFilter, CasProcessingFilter, BasicProcessingFilter, HttpRequestIntegrationFilter, JbossIntegrationFilter 等 - 修改SecurityContextHolder,使其中包含一个有效的认证请求令牌(token)。
5.SecurityContextHolderAwareRequestFilter, 如果你使用它来在你的servlet容器中安装一个Acegi Security aware HttpServletRequestWrapper。
6.RememberMeProcessingFilter, 如果早期的认证处理过程没有更新SecurityContextHolder,并且请求(request)提供了一个cookie启用remember- me服务,一个合适的被记住的Authentication对象会被放到SecurityContextHolder那里。
7.AnonymousProcessingFilter, 如果早期的认证处理过程没有更新SecurityContextHolder,, 一个匿名Authentication 对象会被放到SecurityContextHolder那里。
8.ExceptionTranslationFilter, 捕获所有的Acegi Security 异常,这样要么返回一个HTTP错误响应或者加载一个对应的AuthenticationEntryPoint。
9.FilterSecurityInterceptor, 保护 web URIs
所有上述的filter使用FilterToBeanProxy或FilterChainProxy。建议在一个应用中使用一个单个的 FilterToBeanProxy代理到一个单个的FilterChainProxy。,在FilterChainProxy中定义所有的Acegi Security Filters。如果你使用SiteMesh,确保Acegi Security filters 在 SiteMesh filters调用前调用。这样使SecurityContextHolder在SiteMesh decorator使用前能够及时被装配。