(翻译)Spring Security-2.0.x参考文档”支持的基础设施“
支持的基础设施
这章里讨论一些Spring Security中用到的基础设施。 如果这个功能跟安全没有直接关系,但是还是包含在Spring Security中了,我们就在这章里讨论它。
6.1. 国际化
Spring Security支持异常信息的国际化,最终用户会很喜欢这点。 如果你的程序是为英语用户设计的,你不需要做任何事,因为默认情况下所有的Spring Security消息都是英文的。 如果你需要支持其他语言,你所需要做的就是看看这节内容。
所有的异常信息都是可以本地化的,包括验证失败和访问被拒绝(验证失败)信息。 开发者和系统发布者关注的异常和日志信息(包括不正确的属性,接口联系分隔,使用错误的构造器,开始验证册书,日志调试等级)等等,不能进行本地化,而是使用英文直接硬编码到了Spring Security代码里。
打开spring-security-core-xx.jar,你可以找到org.springframework.security包,里面包含了一个messages.properties文件。 你的ApplicationContext应用应该引用这个文件,因为Spring Security实现了Spring的MessageSourceAware接口,希望这些消息会在application context启动的时候注入到程序中。 通常,你需要做的就是,在你的application context 里注册一个bean,来引用这些信息,比如像下面这样:
<bean id="messageSource" value="org/springframework/security/messages"/>
</bean>
这个messages.properties使用了标准的资源束命名方式,为Spring Security提供了默认的语言支持。 默认的语言是英文。 如果你没有注册消息源,Spring Security依然可以正常工作,但是使用的是硬编码的英文信息版本。
如果你想自定义messages.properties文件,或支持其他语言,你应该复制这个文件,改成对应的文件名,并把它注册到上面的bean定义中。 文件里的关键信息并不多,本地化应该不是一个很大的任务。 如果你对这个文件进行了本地化,请考虑把你的工作与社区分享,记录一个JIRA任务,把你的本地化版本messages.properties以附件形式发送上来。
围绕本地化进行讨论,就要提到Spring里的ThreadLocal,org.springframework.context.i18n.LocaleContextHolder。 你应该通过LocaleContextHolder,来表现每个用户想要的Locale. Spring Security尝试从信息源中定位信息,使用从ThreadLocal里中获得的Locale。 请参考Spring文档获得使用LocaleContextHolder的更多信息,可以使用一些帮助类进行自动设置(比如AcceptHeaderLocaleResolver, CookieLocaleResolver, FixedLocaleResolver, SessionLocaleResolver等等)。
6.2. 过滤器
Spring Security用到了很多过滤器,参考指南的后续部分会一一提到。 如果你使用了命名空间配置,你就不用经常去明确指定过滤器bean。 有几种可能情况,你希望对安全过滤器链进行完全控制,或许因为你使用的功能没法使用命名空间进行支持,或者你使用了自己自定义版本的类。
这种情况下,你可以选择向你的web应用成立里添加哪些过滤器,这里你可以使用Spring的DelegatingFilterProxy或 FilterChainProxy。我们会在下面介绍它们两个。
在使用DelegatingFilterProxy的时候,你会看到web.xml里这样的内容:
<filter>
<filter-name>myFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
注意这个过滤器其实是一个DelegatingFilterProxy,这个过滤器里没有实现过滤器的任何逻辑。 DelegatingFilterProxy做的事情是代理Filter的方法,从application context里获得bean。 这让bean可以获得spring web application context的生命周期支持,使配置较为轻便。 bean必须实现javax.servlet.Filter接口,它必须和filter-name里定义的名称是一样的。
在生命周期的问题上,要考虑在IoC容器里而不是在servlet容器里管理Filter。 具体来说,到底是哪个容器应该调用Filter的“启动”与“关闭”方法。 需要指出的是Filter的初始化和销毁很容易受servlet容器的影响,如果一个Filter依赖于较早初始化Filter的配置,那么可能会引发一些问题。 Spring IoC容器,从另一方面讲,拥有更强大的生命周期/IoC接口(比如InitializingBean, DisposableBean, BeanNameAware, ApplicationContextAware和很多其他的),拥有更容易理解的接口协议,可预见的方法调用顺序,支持自动绑定,更可以选择不用实现Spring的接口(比如通过Spring XML中的destroy-method属性)。 介于这些原因,只要有可能的话,我们推荐使用Spring生命周期服务,代替servlet容器的生命周期。 可以参考DelegatingFilterProxy的Javadoc获得更多信息。
最好别用DelegatingFilterProxy,我们强烈推荐你使用FilterChainProxy代替它。 虽然DelegatingFilterProxy是一个非常有用的类,问题是在需要使用几个过滤器的时候,需要在web.xml中定义<filter>和<filter-mapping>入口的代码数量太多了。 为了解决这个问题,Spring Security提供了一个FilterChainProxy类。 它绑定了一个DelegatingFilterProxy(好像上面的例子那样),但是使用的类是org.springframework.security.util.FilterChainProxy。 过滤器链要声明在application context里,使用下面的代码:
<bean id="filterChainProxy" class="org.springframework.security.util.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/webServices/**"
filters="httpSessionContextIntegrationFilterWithASCFalse,basicProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor"/>
<sec:filter-chain pattern="/**"
filters="httpSessionContextIntegrationFilterWithASCTrue,authenticationProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor"/>
</sec:filter-chain-map>
</bean>
你可能注意到FilterSecurityInterceptor声明的不同方式。 它同时支持正则表达式和ant路径,并且只使用第一个出现的匹配URI。 在运行阶段FilterChainProxy会定位当前web请求匹配的第一个URI模式。 每个与bean相关的配置属性,都定义在application context中。 这些过滤器会按照它们指定的顺序依次调用,使用标准的FilterChain预期行为(如果一个Filter希望中止过滤器连,就可以决定不继续执行)。
你可以看到,FilterChainProxy需要在不同的请求模式中重复引用过滤器的名称(如上例exceptionTranslationFilter 和 filterSecurityInterceptor都重复使用了)。 这个设计思路是让FilterChainProxy可以为不同的URI模式,指定不同的Filter顺序,也可以提升表现(使用正则表达式,ant路径,和其他自定义FilterInvocationDefinitionSource实现),也能弄清楚哪个 Filter应该被调用。
你可能注意到了,我们在过滤器链里声明了两个HttpSessionContextIntegrationFilter(ASC是allowSessionCreation的简写,是HttpSessionContextIntegrationFilter的一个属性)。 因为web服务从来不会在请求里带上jsessionid,为每个用户代理都创建一个HttpSession完全是一种浪费。 如果你需要构建一个高等级最高可扩展性的系统,我们推荐你使用上面的配置方法。 对于小一点儿的项目,使用一个HttpSessionContextIntegrationFilter(让它的allowSessionCreation默认为true)就足够了。
在有关声明周期的问题上,如果这些方法被FilterChainProxy自己调用,FilterChainProxy会始终根据下一层的Filter代理init(FilterConfig)和destroy()方法。 这时,FilterChainProxy会保证初始化和销毁操作只会在Filter上调用一次,而不管它们被FilterInvocationDefinitionSource声明了多少次。 你可以完全控制是否调用这些方法,通过代理DelegatingFilterProxy的targetFilterLifecycle初始化参数,。 像上面讨论的那样,默认的servlet容器生命周期调用不会被DelegatingFilterProxy代理。
同样的,我们可以使用filters = "none"属性,在使用命名空间配置的时候,你可以忽略过滤器链中的一个URI模式,在<URI Pattern> = <Filter Chain>表达式的右手侧使用 #NONE#标志。 比如,使用上边的例子,如果你想排除完全/webservices部分,你可以把bean声明的相关那行修改成这样。
/webServices/**=#NONE#
注意,任何匹配这个路径的请求,不会要求认证,不会使用验证服务,可以自由的访问。
定义在web.xml里的过滤器的顺序是非常重要的。 不论你实际使用的是哪个过滤器,<filter-mapping>的顺序应该像下面这样:
1.
ChannelProcessingFilter,因为它可能需要重定向到其他协议。
2.
ConcurrentSessionFilter,因为它不使用SecurityContextHolder功能,但是需要更新 SessionRegistry 来从主体中放映正在进行的请求。
3.
HttpSessionContextIntegrationFilter,这样 SecurityContext可以在web请求的开始阶段通过 SecurityContextHolder建立,然后 SecurityContext的任何修改都会在web请求结束的时候(为下一个web请求做准备)复制到 HttpSession中。
4.
验证执行机制 - AuthenticationProcessingFilter, CasProcessingFilter, BasicProcessingFilter, HttpRequestIntegrationFilter, JbossIntegrationFilter 等等 - 这样 SecurityContextHolder 可以被修改,并包含一个合法的 Authentication 请求标志。
5.
SecurityContextHolderAwareRequestFilter,如果,你使用它,把一个Spring Security提醒HttpServletRequestWrapper安装到你的servlet容器里。
6.
RememberMeProcessingFilter,这样如果之前的验证执行机制没有更新 SecurityContextHolder,这个请求提供了一个可以使用的remember-me服务的cookie,一个对应的已保存的 Authentication对象会被创建出来。
7.
AnonymousProcessingFilter,这样如果之前的验证执行机制没有更新 SecurityContextHolder,会创建一个匿名 Authentication对象。
8.
ExceptionTranslationFilter,用来捕捉 Spring Security异常,这样,可能返回一个HTTP错误响应,或者执行一个对应的 AuthenticationEntryPoint。
9.
FilterSecurityInterceptor,保护web URI。
上面所有的过滤器,都使用了 DelegatingFilterProxy 或 FilterChainProxy。推荐使用单独的 DelegatingFilterProxy 为每个程序代理一个单独的 FilterChainProxy,通过这个 FilterChainProxy 定义Spring Security的所有过滤器。
如果你使用了SiteMesh,一定要确保Spring Security过滤器在SiteMesh的过滤器之前被调用。 这才可以保证为SiteMesh渲染器使用的 SecurityContextHolder 先被组装起来。
6.3. 标签库
Spring Security捆绑了很多JSP标签库,提供了各种不同的服务。
6.3.1. 配置
所有的taglib类都包含在核心spring-security-xx.jar文件里, security.tld文件放在JAR的META-INF目录下。 这意味着,对于JSP 1.2+的web容器,你可以直接把JAR放到WAR的WEB-INF/lib目录下,然后就可以用了。 如果你使用的是JSP 1.1 容器,你需要在你的web.xml文件里声明JSP taglib,还要把security.tld放到WEB-INF/lib目录下。 把下面的片段添加到web.xml里:
<taglib>
<taglib-uri>http://www.springframework.org/security/tags</taglib-uri>
<taglib-location>/WEB-INF/security.tld</taglib-location>
</taglib>
6.3.2. 使用
现在你已经配置好了标签库,可以转到单独的参考指南章节,阅读如何使用他们的详细信息。 注意当使用标签时,你应该在你的JSP里包含taglib引用:
<%@ taglib prefix='security' uri='http://www.springframework.org/security/tags' %>