有的情况下,你需要使用Spring Security进行认证,但是用户已经在访问系统之前,在一些外部系统中认证过了。 我们把这种情况叫做“预认证”场景。 例子包括X.509,Siteminder和应用所在的J2EE容器进行认证。 在使用预认证的使用,Spring Security必须
定义使用请求的用户
从用户里获得权限
细节信息要依靠外部认证机制。
一个用户可能,在X.509的情况下由认证信息确定,或在Siteminder的情况下使用HTTP请求头。
对于容器认证,需要调用获得HTTP请求的getUserPrincipal()
方法来确认用户。
一些情况下,外部机制可能为用户提供角色/权限信息,其他情况就需要通过单独信息源获得,比如UserDetailsService
。
因为大多数预认证机制都遵循相同的模式,所以Spring Security提供了一系列的类,它们作为内部框架实现预认证认证提供器。 这样就避免了重复实现,让新实现很容易添加到结构中,不需要一切从脚手架开始写起。 你不需要知道这些类,如果你想使用一些东西,比如X.509认证,因为它已经是命名空间配置里的一个选项了,可以很简单的使用,启动它。 如果你需要使用精确的bean配置,或计划编写你自己的实现,这时了解这些提供的实现是如何工作就很有用了。 你会在org.springframework.security.web.authentication.preauth包下找到这些类。 我们这里只提供一个纲要,你应该从对应的Javadoc和源代码里获得更多信息。
这个类会检测安全环境的当前内容,如果是空的,它会从HTTP请求里获得信息,提交给AuthenticationManager
。
子类重写了以下方法来获得信息:
protected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest request); protected abstract Object getPreAuthenticatedCredentials(HttpServletRequest request);
在调用之后,过滤器会创建一个包含了返回数据的PreAuthenticatedAuthenticationToken
,然后提交它进行认证。
通过这里的“authentication”,我们其实只是可能进行读取用户的权限,不过下面就是标准的Spring Security认证结构了。
就像其他的Spring Security认证过滤器一样,预认证过滤器有一个authenticationDetailsSource
属性,默认会创建一个WebAuthenticationDetails
对象来保存额外的信息,比如在Authentication
对象的details
属性里的会话标识,原始IP地址。
用户角色信息可以从预认证机制中获得,数据也保存在这个属性里。
AbstractPreAuthenticatedAuthenticationDetailsSource
的子类,使用实现了GrantedAuthoritiesContainer
接口的扩展信息,因此可以使用认证提供其器来读取权限,明确定位用户。
下面我们看一个具体的例子。
如果过滤器配置了authenticationDetailsSource
的实例,通过调用isUserInRole(String role)
方法为每个预先决定的“可映射角色”集合获得认证信息。
这个类从MappableAttributesRetriever
里获得这些信息。
可能的实现方法,包含了在application context中进行硬编码,或者从web.xml
的<security-role>
中读取角色信息。
预认证例子程序使用了后一种方式。
这儿有一个额外的步骤,使用一个Attributes2GrantedAuthoritiesMapper
把角色(或属性)映射到Spring Security的GrantedAuthority
。
它默认只会为名称添加一个ROLE_
前缀,但是你可以对这些行为进行完全控制。
预认证提供器除了从用户中读取UserDetails
以外,还要一些其他事情。
它通过调用一个AuthenticationUserDetailsService
来做这些事情。
后者就是一个标准的UserDetailsService
,但要需要的参数是一个Authentication
对象,而不是用户名:
public interface AuthenticationUserDetailsService { UserDetails loadUserDetails(Authentication token) throws UsernameNotFoundException; }
这个接口可能也含有其他用户,但是预认证允许访问权限,打包在Authentication
对象里,像上一节所见的。
这个PreAuthenticatedGrantedAuthoritiesUserDetailsService
就是用来作这个的。
或者,它可能调用标准UserDetailsService
,使用UserDetailsByNameServiceWrapper
这个实现。
这个 AuthenticationEntryPoint
在 技术概述 那章讨论过。
通常它用来为未认证用户(当他们想访问被保护资源的时候)启动认证过程,但是在预认证情况下这不会发生。
如果你不使用预认证结合其他认证机制的话,你只要配置ExceptionTranslationFilter
的一个实例。
如果用户的访问被拒绝了,它就会调用,AbstractPreAuthenticatedProcessingFilter
结果返回的一个空的认证。
调用的时候,它总会返回一个403
禁用响应代码。
X.509认证写在它自己的章里。 这里,我们看一些支持其他预认证的场景。
一个外部认证系统可以通过在HTTP请求里设置特殊的头信息,给应用提供信息。
一个众所周知的例子就是Siteminder,它在头部传递用户名,叫做SM_USER
。
这个机制被RequestHeaderPreAuthenticatedProcessingFilter
支持,直接从头部得到用户名。
默认使用SM_USER
作为头部名。
看一下Javadoc获得更多信息。
注意使用这种系统时,框架不需要作任何认证检测,极端重要的是,要把外部系统配置好,保护系统的所有访问。 如果攻击者可以从原始请求中获得请求头,不通过检测,他们可能潜在修改任何他们想要的用户名。
使用这个过滤器的典型配置应该像这样:
<security:http> <!-- Additional http configuration omitted --> <security:custom-filter ref="siteminderFilter" /> </security:http> <bean id="siteminderFilter" class="org.springframework.security.web.authentication.preauth.header.RequestHeaderAuthenticationFilter"> <property name="principalRequestHeader" value="SM_USER"/> <property name="authenticationManager" ref="authenticationManager" /> </bean> <bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider"> <property name="preAuthenticatedUserDetailsService"> <bean id="userDetailsServiceWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper"> <property name="userDetailsService" ref="userDetailsService"/> </bean> </property> </bean> <security:authentication-manager alias="authenticationManager"> <security:authentication-provider ref="preauthAuthProvider" /> </security-authentication-manager>
我们假设使用安全命名空间的配置方式,custom-filter
,使用了authentication-manager
和custom-authentication-provider
三个元素(你可以从命名空间章节里了解它们的更多信息)。
你应该走出传统的配置方式。
我们也假设了你在配置里添加了一个UserDetailsService
(名叫“userDetailsService”),来读取用户的角色信息。
这个J2eePreAuthenticatedProcessingFilter
类会从HttpServletRequest
的userPrincipal
属性里获得准确的用户名。
这个过滤器的用法常常结合使用J2EE角色,像上面描述的Section 17.1.2.1, “J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource”。
在根目录下有一个使用这个功能的实例应用,可以从svn里找到,
如果你感兴趣的话,可以看一下application context文件。
代码在samples/preauth
目录下。