(通译)Spring Security-2.0.x参考文档“预认证场景”

(翻译)Spring Security-2.0.x参考文档“预认证场景”
预认证场景

有的情况下,你需要使用Spring Security进行认证,但是用户已经在访问系统之前,在一些外部系统中认证过了。 我们把这种情况叫做“预认证”场景。 例子包括X.509,Siteminder和应用所在的J2EE容器进行认证。 在使用预认证的使用,Spring Security必须

   1.

      定义使用请求的用户
   2.

      从用户里获得权限

细节信息要依靠外部认证机制。 一个用户可能,在X.509的情况下由认证信息确定,或在Siteminder的情况下使用HTTP请求头。 对于容器认证,需要调用获得HTTP请求的getUserPrincipal()方法来确认用户。 一些情况下,外部机制可能为用户提供角色/权限信息,其他情况就需要通过单独信息源获得,比如UserDetailsService。
16.1. 预认证框架类

因为大多数预认证机制都遵循相同的模式,所以Spring Security提供了一系列的类,它们作为内部框架实现预认证认证提供器。 这样就避免了重复实现,让新实现很容易添加到结构中,不需要一切从脚手架开始写起。 你不需要知道这些类,如果你想使用一些东西,比如X.509认证,因为它已经是命名空间配置里的一个选项了,可以很简单的使用,启动它。 如果你需要使用精确的bean配置,或计划编写你自己的实现,这时了解这些提供的实现是如何工作就很有用了。 你会在org.springframework.security.ui.preauth包下找到web相关的类,后台类都在org.springframework.security.providers.preauth包里。 我们这里只提供一个纲要,你应该从对应的Javadoc和源代码里获得更多信息。
16.1.1. AbstractPreAuthenticatedProcessingFilter

这个类会检测安全环境的当前内容,如果是空的,它会从HTTP请求里获得信息,提交给AuthenticationManager。 子类重写了以下方法来获得信息:

  protected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest request);

  protected abstract Object getPreAuthenticatedCredentials(HttpServletRequest request);

在调用之后,过滤器会创建一个包含了返回数据的PreAuthenticatedAuthenticationToken,然后提交它进行认证。 通过这里的“authentication”,我们其实只是可能进行读取用户的权限,不过下面就是标准的Spring Security认证结构了。
16.1.2. AbstractPreAuthenticatedAuthenticationDetailsSource

就像其他的Spring Security认证过滤器一样,预认证过滤器有一个authenticationDetailsSource属性,默认会创建一个WebAuthenticationDetails对象来保存额外的信息,比如在Authentication对象的details属性里的会话标识,原始IP地址。 用户角色信息可以从预认证机制中获得,数据也保存在这个属性里。 AbstractPreAuthenticatedAuthenticationDetailsSource的子类,使用实现了GrantedAuthoritiesContainer接口的扩展信息,因此可以使用认证提供其器来读取权限,明确定位用户。 下面我们看一个具体的例子。
16.1.2.1. J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource

如果过滤器配置了authenticationDetailsSource的实例,通过调用isUserInRole(String role)方法为每个预先决定的“可映射角色”集合获得认证信息。 这个类从MappableAttributesRetriever里获得这些信息。 可能的实现方法,包含了在application context中进行硬编码,或者从web.xml的<security-role>中读取角色信息。 预认证例子程序使用了后一种方式。

这儿有一个额外的步骤,使用一个Attributes2GrantedAuthoritiesMapper把角色(或属性)映射到Spring Security的GrantedAuthority。 它默认只会为名称添加一个ROLE_前缀,但是你可以对这些行为进行完全控制。
16.1.3. PreAuthenticatedAuthenticationProvider

预认证提供器除了从用户中读取UserDetails以外,还要一些其他事情。 它通过调用一个AuthenticationUserDetailsService来做这些事情。 后者就是一个标准的UserDetailsService,但要需要的参数是一个Authentication对象,而不是用户名:

  public interface AuthenticationUserDetailsService {
    UserDetails loadUserDetails(Authentication token) throws UsernameNotFoundException;
  }

这个接口可能也含有其他用户,但是预认证允许访问权限,打包在Authentication对象里,像上一节所见的。 这个PreAuthenticatedGrantedAuthoritiesUserDetailsService就是用来作这个的。 或者,它可能调用标准UserDetailsService,使用UserDetailsByNameServiceWrapper这个实现。
16.1.4. PreAuthenticatedProcessingFilterEntryPoint

这个 AuthenticationEntryPoint 在 技术概述 那章讨论过。 通常它用来为未认证用户(当他们想访问被保护资源的时候)启动认证过程,但是在预认证情况下这不会发生。 如果你不使用预认证结合其他认证机制的话,你只要配置ExceptionTranslationFilter的一个实例。 如果用户的访问被拒绝了,它就会调用,AbstractPreAuthenticatedProcessingFilter结果返回的一个空的认证。 调用的时候,它总会返回一个403禁用响应代码。
16.2. 具体实现

X.509认证写在它自己的章里。 这里,我们看一些支持其他预认证的场景。
16.2.1. 请求头认证(Siteminder)

一个外部认证系统可以通过在HTTP请求里设置特殊的头信息,给应用提供信息。 一个众所周知的例子就是Siteminder,它在头部传递用户名,叫做SM_USER。 这个机制被RequestHeaderPreAuthenticatedProcessingFilter支持,直接从头部得到用户名。 默认使用SM_USER作为头部名。 看一下Javadoc获得更多信息。
Tip

注意使用这种系统时,框架不需要作任何认证检测,极端重要的是,要把外部系统配置好,保护系统的所有访问。 如果攻击者可以从原始请求中获得请求头,不通过检测,他们可能潜在修改任何他们想要的用户名。
16.2.1.1. Siteminder示例配置

使用这个过滤器的典型配置应该像这样:

    <bean id="siteminderFilter"
      class="org.springframework.security.ui.preauth.header.RequestHeaderPreAuthenticatedProcessingFilter">
    <security:custom-filter position="PRE_AUTH_FILTER" />
    <property name="principalRequestHeader" value="SM_USER"/>
    <property name="authenticationManager" ref="authenticationManager" />
  </bean>

  <bean id="preauthAuthProvider"
      class="org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationProvider">
    <security:custom-authentication-provider />
    <property name="preAuthenticatedUserDetailsService">
      <bean id="userDetailsServiceWrapper"
            class="org.springframework.security.userdetails.UserDetailsByNameServiceWrapper">
        <property name="userDetailsService" ref="userDetailsService"/>
      </bean>
    </property>
    </bean>

    <security:authentication-manager alias="authenticationManager" />

我们假设使用安全命名空间的配置方式,custom-filter,使用了authentication-manager和custom-authentication-provider三个元素(你可以从命名空间章节里了解它们的更多信息)。 你应该走出传统的配置方式。 我们也假设了你在配置里添加了一个UserDetailsService(名叫“userDetailsService”),来读取用户的角色信息。
16.2.2. J2EE容器认证

这个J2eePreAuthenticatedProcessingFilter类会从HttpServletRequest的userPrincipal属性里获得准确的用户名。 这个过滤器的用法常常结合使用J2EE角色,像上面描述的Section 16.1.2.1, “J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource”。