28. Pre-Authentication Scenarios(预认证场景)
有些情况下,您希望使用Spring Security进行授权,但是在访问应用程序之前,用户已经被某个外部系统可靠地验证过了。我们将这些情况称为“预认证”场景。示例包括X.509、Siteminder和应用程序运行所在的Java EE容器的身份验证。使用预认证时,Spring Security必须
1、识别提出请求的用户。
2、为用户获取权限。
细节将取决于外部认证机制。在X.509的情况下,可以通过用户的证书信息来识别用户,在Siteminder的情况下,可以通过HTTP请求头来识别用户。如果依赖容器身份验证,将通过对传入的HTTP请求调用getUserPrincipal()方法来识别用户。在某些情况下,外部机制可能为用户提供角色/权限信息,但在其他情况下,权限必须从单独的来源获得,如UserDetailsService。
28.1 Pre-Authentication Framework Classes(预认证框架类别)
因为大多数预认证机制遵循相同的模式,所以Spring Security有一组类,它们为实现预认证的认证提供者提供了一个内部框架。这消除了重复,并允许以结构化的方式添加新的实现,而不必从头开始编写所有内容。如果您想使用类似X.509身份验证的东西,您不需要知道这些类,因为它已经有了一个名称空间配置选项,使用起来和开始时都更简单。如果您需要使用显式bean配置,或者正在计划编写自己的实现,那么了解所提供的实现是如何工作的将是非常有用的。您将在org.springframework.security.web.authentication.preauth下找到类。我们只是在这里提供了一个概要,因此您应该在适当的地方查阅Javadoc和源代码。
28.1.1 AbstractPreAuthenticatedProcessingFilter抽象预认证流程过滤器
此类将检查安全上下文的当前内容,如果为空,它将尝试从HTTP请求中提取用户信息,并将其提交给身份验证管理器。子类重写以下方法来获取此信息:
调用这些之后,过滤器将创建一个包含返回数据的预验证身份验证令牌(PreAuthenticatedAuthenticationToken ),并将其提交进行身份验证。这里所说的“身份验证”,我们实际上只是指进一步的处理,也许是加载用户的权限,但是遵循标准的Spring Security身份验证架构。与其他Spring Security身份验证筛选器一样,预身份验证筛选器具有authenticationDetailsSource属性,默认情况下,该属性将创建一个WebAuthenticationDetails对象,以在身份验证对象Authentication 的Details属性中存储附加信息,如会话标识符和源IP地址。在可以从预认证机制获得用户角色信息的情况下,数据也存储在该属性中,细节实现了GrantedAuthoritiesContainer接口。这使得认证提供者能够读取外部分配给用户的权限。接下来我们将看一个具体的例子。
J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource 基于J2ee的预认证网络认证详细信息来源
如果筛选器配置了身份验证详细信息资源(authenticationDetailsSource )(该资源是此类的一个实例),则通过为一组预先确定的“可映射角色”中的每一个调用isUserInRole(字符串角色)方法来获取权限信息。该类从已配置的MappableAttributesRetriever中获取这些信息。可能的实现包括在应用程序上下文中对列表进行硬编码,以及从web.xml文件中的<security-role>信息中读取角色信息。预认证示例应用程序使用后一种方法。还有一个阶段,使用配置的Attributes2GrantedAuthoritiesMapper将角色(或属性)映射到Spring Security GrantedAuthority对象。默认情况下,只需在名称中添加通常的ROLE_ 前缀,但它可以让您完全控制行为。
28.1.2 PreAuthenticatedAuthenticationProvider
预身份验证身份验证提供程序
预认证的提供者除了为用户加载UserDetails对象之外,没有什么可做的。它通过委托给身份验证用户详细信息服务(AuthenticationUserDetailsService)来实现这一点。后者类似于标准的用户详细信息服务(UserDetailsService ),但采用一个身份验证对象,而不仅仅是用户名:
这个接口可能还有其他用途,但是通过预认证,它允许访问封装在认证对象中的权限,正如我们在上一节中看到的。PreAuthenticatedGrantedAuthoritiesUserDetailsService 类执行此操作。或者,它可以通过UserDetailsByNameServiceWrapper 实现委托给标准的用户详细信息服务(UserDetailsService )。
28.1.3 Http403ForbiddenEntryPoint
技术概述一章中讨论了身份验证入口点(AuthenticationEntryPoint )。通常情况下,它负责为未经身份验证的用户启动身份验证过程(当他们试图访问受保护的资源时),但是在预身份验证的情况下,这并不适用。如果没有将预身份验证与其他身份验证机制结合使用,则只能使用此类的实例来配置异常转换筛选器(ExceptionTranslationFilter )。如果用户被抽象预身份验证进程筛选器(AbstractPreAuthenticatedProcessingFilter )拒绝,导致空身份验证,将调用该函数。如果被调用,它总是返回一个403-禁止响应代码。
28.2 Concrete Implementations(具体实施)
X.509身份验证包含在它自己的章节中。在这里,我们将看一些为其他预认证场景提供支持的类。
28.2.1 Request-Header Authentication (Siteminder)
请求-报头认证(网站管理员)
外部认证系统可以通过在超文本传输协议请求上设置特定的头来向应用程序提供信息。一个众所周知的例子是Siteminder,它在一个名为SM_USER的头中传递用户名。RequestHeaderauthenticationFilter类支持这种机制,它只是从头部提取用户名。它默认使用名称SM_USER作为标题名称。有关更多详细信息,请参见Javadoc。
请注意,当使用这样的系统时,框架根本不执行任何身份验证检查,外部系统正确配置并保护对应用程序的所有访问非常重要。如果攻击者能够在其原始请求中伪造报头而不被检测到,那么他们可能会选择他们想要的任何用户名。
Siteminder Example Configuration
使用此过滤器的典型配置如下所示:
我们在这里假设安全命名空间用于配置。还假设您已经在配置中添加了一个用户详细信息服务(UserDetailsService )(称为“用户详细信息服务”)来加载用户的角色。
28.2.2 Java EE Container Authentication(Java EE容器认证)
J2ee PreAuthenticatedProcessingFilter类将从HttpServletRequest的userPrincipal属性中提取用户名。该过滤器的使用通常与Java EE角色的使用相结合,如上面在“J2ee basedpreauthenticationDetailsSource”一节中所述。
代码库中有一个使用这种方法的示例应用程序,因此如果您感兴趣,可以从github获得代码并查看应用程序上下文文件。代码在samples/xml/preauth目录中。