预认证场景

示例包括 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。子类重写以下方法以获取此信息。

重写 AbstractPreAuthenticatedProcessingFilter
  • 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文件中的<security-role>信息中读取角色信息。预身份验证示例应用程序使用后一种方法。

还有一个附加阶段,其中角色(或属性)通过使用配置的Attributes2GrantedAuthoritiesMapper映射到Spring Security GrantedAuthority对象。默认情况下只是在名称前添加通常的ROLE_前缀,但它允许您完全控制行为。

预身份验证提供程序

预身份验证提供程序只需为用户加载UserDetails对象。它通过委托给AuthenticationUserDetailsService来实现此目的。后者类似于标准的UserDetailsService,但它接受Authentication对象,而不仅仅是用户名。

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

此接口可能还有其他用途,但是,对于预身份验证,它允许访问打包在Authentication对象中的权限,正如我们在上一节中看到的那样。PreAuthenticatedGrantedAuthoritiesUserDetailsService类就是这样做的。或者,它可以通过UserDetailsByNameServiceWrapper实现委托给标准的UserDetailsService

Http403ForbiddenEntryPoint

AuthenticationEntryPoint 负责启动未经身份验证的用户(当他们尝试访问受保护的资源时)的身份验证过程。但是,在预身份验证的情况下,这并不适用。只有当您不将预身份验证与其他身份验证机制结合使用时,才将ExceptionTranslationFilter与此类的实例一起配置。如果调用它,则表示用户被AbstractPreAuthenticatedProcessingFilter拒绝,导致身份验证为null。它始终返回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类从HttpServletRequestuserPrincipal属性中提取用户名。此过滤器的使用通常与Java EE角色的使用相结合,如前面J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource中所述。

代码库中有一个示例应用程序使用这种方法,因此如果您感兴趣,请从Github获取代码并查看应用程序上下文文件。