当前位置 博文首页 > 适己而忘人者,人之所弃;克己而立人者,众之所戴。:知识积累(
第六章. 通用认证服务
6.1. Mechanisms, Providers 和 Entry Points
如果你使用Acegi Security提供的认证方法,那么通常你需要配置一个web filter,一个AuthenticationProvider
以及AuthenticationEntryPoint。在本节我们将要浏览一个示例应用,它需要支持基于form的认证(例如提供给用户登录的HTML页面)以及基础认证(例如web service或者类似的可以访问受保护资源)。
?
在web.xml中,这个应用需要一个单独的Acegi Security filter来使用FilterChainProxy。几乎所有的Acegi Security应用都有一个类似的项,看起来象下面这样:
?
xml 代码
?
上述声明将使每个web请求都要经过Acegi Security的FilterChainProxy。正如在本手册的filter那节中所说,FilterChainProxy是一个通用类,它使得web请求按照URL模式被发送到不同的filter。那些被委派的filter是由application context管理的,因此它们可以享受依赖注射的好处。我们来看看在你的application context中FilterChainProxy的定义会是什么样的:
?
xml 代码
?
在内部,Acegi Security会使用PropertyEditor来将上述XML片段中的字符串转化为一个FilterInvocationDefinitionSource对象。在这个阶段需要注意的是,一系列的filter会按照定义的顺序运行,并且这些filter实际就是application context中的bean的。所以,在我们的例子中,会在application context出现另外一些bean,它们会被命名为httpSessionContextIntegrationFilter, logoutFilter 等。Filter出现的顺序会在手册中filter那一节讨论,虽然上述的例子中它们是正确的。
?
在我们的例子中,我们使用了AuthenticationProcessingFilter和BasicProcessingFilter。它们分别对应了基于form的认证和BASIC HTTP header-based认证的“认证机制”(我们在手册的前面部分讨论了认证机制扮演的角色)。如果你既不使用form也不使用BASIC认证,就不需要定义这些bean了。取而代之的是你要定义对应你所需要的认证环境的filter,例如DigestProcessingFilter 或者CasProcessingFilter。请对照手册中对应的章节来了解如何配置这些认证机制。
?
让我们回忆一下,在HttpSessionContextIntegrationFilter中保存了每个HTTP session调用中的SecurityContext。这意味着认证机制只会在principal最初尝试认证的时候被使用一次。在余下的时间内,认证机制只是静静的待在那里,将请求发往filter链中的下一个filter。这个基于实际的需求源于这样的一个事实,很少有认证实现在每一个,每一次的调用的时候都会进行认证(BASIC认证是一个值得注意的例外),但是如果一个pricipal在最初的认证步骤之后帐号被取消了,或者被禁用了,或者被修改了(例如GrantedAuthority[]中增加或者减少)会怎么样呢?让我们来看看现在这些情况是如何处理的。
?
前面已经介绍了安全对象的主要认证provider AbstractSecurityInterceptor。这个类需要能够访问一个AuthenticationManager。它同时有个可选配置可以设定一个认证对象每次安全对象调用的时候是否需要重新认证。如果Authentication.isAuthenticated()返回true,那么它默认在SecurityContextHolder中的认证对象是已认证的。这样做对于提高性能是非常好的,但是对于即时的认证验证是不理想的。在这样的情况下你可能需要将AbstractSecurityInterceptor.alwaysReauthenticate属性设置为true。
?
你可能会问自己“这个AuthenticationManager是什么?”我们之前没有见过它,但是我们曾经讨论过AuthenticationProvider的概念。非常简单,AuthenticationManager负责在AuthenticationProvider链之间传递请求。它非常象我们之前讨论过的filter链,虽然有一些不同。Acegi Security只提供了一个AuthenticationManager实现,因此让我们看看对于我们这章的例子,它是如何配置的:
?
xml 代码
?
在这个时候,可能值得提到的是你的认证机制(通常是filter)也被注入了一个AuthenticationManager的引用。所以和认证机制都会使用上述的ProviderManager来轮询一系列的AuthenticationProvider。
?
在我们例子中有三个provider。它们按照上述的顺序调用(使用list而不是set来显示是按照顺序调用的),每个provider都能够尝试认证,或者仅仅返回一个null来跳过认证。如果所有的实现都返回null,ProviderManager会抛出一个相应的异常。如果你想了解更多chaining providers的信息,请参阅ProviderManager的JavaDoc。
?
authentication mechanism使用的那些provider有时候是可以互换的,而有时候它们又依赖于特定的authentication mechanism。例如,DaoAuthenticationProvider只需要一个基于字符串的用户名和密码。若干个认证机制会产生基于字符串的用户名和密码的集合,包括(但不限于)BASIC 和 form 认证。同时,有些认证机制会产生一个只能和特定类型的AuthenticationProvider交互的认证请求对象。一个这种一对一映射的例子是JA-SIG CAS,它使用service ticket的概念,只能被Common Authentication Services CasAuthenticationProvider认证。一个更加深入的一对一映射的例子是LDAP认证机制,它只能由LdapAuthenticationProvider处理。这种特定的对应关系在每个类的JavaDoc以及在本手册的特定认证方法章节中有详细说明。你不用担心这些实现的细节,因为如果你忘记注册一个合适的provider,你在尝试认证时只会收到一个ProviderNotFoundException异常。
?
当你在FilterChainProxy中正确配置了认证机制,并且确保注册了对应的AuthenticationProvider,你的最后一步是配置一个AuthenticationEntryPoint。回忆一下早先我们讨论过的ExceptionTranslationFilter的角色,当一个基于HTTP的请求收到一个HTTP头或者一个HTTP重定向以开始认证时它被使用。继续我们早先的例子:
?
xml 代码