预身份验证场景
示例包括 X.509、Siteminder 以及应用程序运行的 Java EE 容器进行的身份验证。使用预身份验证时,Spring Security 必须
-
识别发出请求的用户。
-
获取用户的权限。
具体细节取决于外部身份验证机制。在 X.509 的情况下,用户可能通过其证书信息进行识别,或者在 Siteminder 的情况下通过 HTTP 请求头进行识别。如果依赖于容器身份验证,则通过在传入的 HTTP 请求上调用 getUserPrincipal()
方法来识别用户。在某些情况下,外部机制可能会为用户提供角色和权限信息。但是,在其他情况下,您必须从其他来源(例如 UserDetailsService
)获取权限。
预身份验证框架类
由于大多数预身份验证机制遵循相同的模式,因此 Spring Security 有一组类,它们提供了一个内部框架来实现预身份验证身份验证提供程序。这消除了重复,并允许以结构化的方式添加新实现,而无需从头开始编写所有内容。如果您想使用诸如 X.509 身份验证 之类的东西,则无需了解这些类,因为它已经有一个命名空间配置选项,该选项更易于使用和入门。如果您需要使用显式 bean 配置或计划编写自己的实现,则需要了解提供的实现的工作原理。您可以在 org.springframework.security.web.authentication.preauth
下找到这些类。我们在这里只提供一个概述,因此您应该在适当的时候查阅 Javadoc 和源代码。
AbstractPreAuthenticatedProcessingFilter
此类检查安全上下文的当前内容,如果为空,则尝试从 HTTP 请求中提取用户信息并将其提交给 AuthenticationManager
。子类覆盖以下方法以获取此信息。
-
Java
-
Kotlin
protected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest request);
protected abstract Object getPreAuthenticatedCredentials(HttpServletRequest request);
protected abstract fun getPreAuthenticatedPrincipal(request: HttpServletRequest): Any?
protected abstract fun getPreAuthenticatedCredentials(request: HttpServletRequest): Any?
在调用这些方法之后,过滤器会创建一个包含返回数据的 PreAuthenticatedAuthenticationToken
,并将其提交以进行身份验证。这里的“身份验证”实际上只是指进一步处理,例如加载用户的权限,但遵循标准的 Spring Security 身份验证体系结构。
与其他 Spring Security 身份验证过滤器一样,预身份验证过滤器具有一个 `authenticationDetailsSource` 属性,该属性默认情况下会创建一个 `WebAuthenticationDetails` 对象,用于在 `Authentication` 对象的 `details` 属性中存储附加信息,例如会话标识符和源 IP 地址。在可以从预身份验证机制获取用户角色信息的情况下,数据也会存储在此属性中,并且详细信息实现了 `GrantedAuthoritiesContainer` 接口。这使身份验证提供者能够读取外部分配给用户的权限。接下来,我们将看一个具体的示例。
J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource
如果过滤器配置了 `authenticationDetailsSource`,它是一个此类的实例,则通过为预先确定的一组“可映射角色”中的每一个调用 `isUserInRole(String role)` 方法来获取权限信息。该类从配置的 `MappableAttributesRetriever` 获取这些角色。可能的实现包括在应用程序上下文中硬编码列表以及从 `web.xml` 文件中的 `
还有一个额外的阶段,其中角色(或属性)通过使用配置的 `Attributes2GrantedAuthoritiesMapper` 映射到 Spring Security `GrantedAuthority` 对象。默认情况下只是在名称前面添加通常的 `ROLE_` 前缀,但它允许您完全控制行为。
PreAuthenticatedAuthenticationProvider
预身份验证提供者只需加载用户的 `UserDetails` 对象。它通过委托给 `AuthenticationUserDetailsService` 来实现这一点。后者类似于标准的 `UserDetailsService`,但它接受一个 `Authentication` 对象,而不是仅仅接受用户名。
public interface AuthenticationUserDetailsService {
UserDetails loadUserDetails(Authentication token) throws UsernameNotFoundException;
}
此接口可能还有其他用途,但对于预身份验证,它允许访问打包在 `Authentication` 对象中的权限,正如我们在上一节中看到的。`PreAuthenticatedGrantedAuthoritiesUserDetailsService` 类就是这样做的。或者,它可以通过 `UserDetailsByNameServiceWrapper` 实现委托给标准的 `UserDetailsService`。
Http403ForbiddenEntryPoint
该 `AuthenticationEntryPoint` 负责启动未经身份验证的用户(当他们尝试访问受保护的资源时)的身份验证过程。但是,在预身份验证情况下,这并不适用。只有在不将预身份验证与其他身份验证机制结合使用的情况下,您才会将 `ExceptionTranslationFilter` 配置为此类的实例。如果用户被 `AbstractPreAuthenticatedProcessingFilter` 拒绝,导致身份验证为空,则会调用它。如果被调用,它始终返回 `403` 禁止响应代码。
具体实现
X.509 身份验证在其单独章节中介绍。在这里,我们将介绍一些支持其他预先身份验证场景的类。
请求头身份验证(Siteminder)
外部身份验证系统可以通过在 HTTP 请求中设置特定头来向应用程序提供信息。一个众所周知的例子是 Siteminder,它在名为 SM_USER
的头中传递用户名。此机制由 RequestHeaderAuthenticationFilter
类支持,该类仅从头中提取用户名。它默认使用 SM_USER
作为头名称。有关更多详细信息,请参阅 Javadoc。
使用此类系统时,框架不会执行任何身份验证检查,并且非常重要的是外部系统配置正确并保护对应用程序的所有访问。如果攻击者能够在未被检测到的情况下伪造其原始请求中的头,他们可能会选择任何他们想要的用户名。 |
Siteminder 示例配置
以下示例显示了使用此过滤器的典型配置
<security:http>
<!-- Additional http configuration omitted -->
<security:custom-filter position="PRE_AUTH_FILTER" ref="siteminderFilter" />
</security:http>
<bean id="siteminderFilter" class="org.springframework.security.web.authentication.preauth.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>
我们假设这里使用了安全命名空间进行配置。还假设您已将 UserDetailsService
(称为“userDetailsService”)添加到您的配置中以加载用户的角色。
Java EE 容器身份验证
J2eePreAuthenticatedProcessingFilter
类从 HttpServletRequest
的 userPrincipal
属性中提取用户名。此过滤器的使用通常会与 Java EE 角色的使用相结合,如前面J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource中所述。
有一个示例应用程序在代码库中使用这种方法,因此请从 Github 获取代码并查看应用程序上下文文件,如果您有兴趣。