对于最新的稳定版本,请使用 Spring Security 7.0.4spring-doc.cadn.net.cn

预认证场景

示例包括 X.509、Siteminder,以及应用程序运行所在的 Java EE 容器进行的身份验证。 使用预认证时,Spring Security 必须:spring-doc.cadn.net.cn

具体细节取决于外部认证机制。 在使用 X.509 的情况下,用户可能通过其证书信息进行识别;而在使用 Siteminder 的情况下,则可能通过 HTTP 请求头进行识别。 如果依赖容器认证,则通过调用传入 HTTP 请求上的 getUserPrincipal() 方法来识别用户。 在某些情况下,外部机制可能会为用户提供角色和权限信息。但在其他情况下,您必须从单独的来源(例如 UserDetailsService)获取权限信息。spring-doc.cadn.net.cn

预认证框架类

由于大多数预认证(pre-authentication)机制遵循相同的模式,Spring Security 提供了一组类,用于构建实现预认证身份验证提供程序的内部框架。 这消除了重复代码,并允许以结构化的方式添加新的实现,而无需从头开始编写所有内容。 如果您只想使用类似 X.509 认证 的功能,则无需了解这些类,因为它已经提供了命名空间配置选项,使用起来更简单,也更容易上手。 但如果您需要使用显式的 Bean 配置,或者计划编写自己的实现,就需要理解所提供的实现是如何工作的。 这些类位于 org.springframework.security.web.authentication.preauth 包下。 我们在此仅提供一个概要,因此在适当的情况下,您应查阅相关的 Javadoc 和源代码。spring-doc.cadn.net.cn

抽象预认证过滤器

该类检查安全上下文的当前内容,如果为空,则尝试从 HTTP 请求中提取用户信息并将其提交给 AuthenticationManager。 子类通过重写以下方法来获取这些信息。spring-doc.cadn.net.cn

重写 AbstractPreAuthenticatedProcessingFilter
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-doc.cadn.net.cn

与其他 Spring Security 认证过滤器一样,预认证过滤器具有一个 authenticationDetailsSource 属性,默认情况下,该属性会创建一个 WebAuthenticationDetails 对象,用于在 details 对象的 Authentication 属性中存储额外信息,例如会话标识符和原始 IP 地址。 在某些情况下,如果用户角色信息可以从预认证机制中获取,这些数据也会存储在此属性中,且详情对象会实现 GrantedAuthoritiesContainer 接口。 这使得认证提供者能够读取外部为用户分配的权限。 接下来,我们将查看一个具体的示例。spring-doc.cadn.net.cn

基于 J2EE 的预认证 Web 认证详情源

如果该过滤器配置了一个 authenticationDetailsSource(即此类的一个实例),则会针对一组预定义的“可映射角色”(mappable roles)逐一调用 isUserInRole(String role) 方法来获取权限信息。 该类通过一个已配置的 MappableAttributesRetriever 获取这些角色。 可能的实现方式包括在应用程序上下文中硬编码一个角色列表,或者从 <security-role> 文件中的 web.xml 信息中读取角色数据。 预认证(pre-authentication)示例应用程序采用的是后一种方法。spring-doc.cadn.net.cn

还有一个额外的阶段,即通过配置的 GrantedAuthority 将角色(或属性)映射为 Spring Security 的 Attributes2GrantedAuthoritiesMapper 对象。 默认情况下,它只是在名称前添加常规的 ROLE_ 前缀,但同时也让你能够完全控制该行为。spring-doc.cadn.net.cn

预认证身份验证提供者

预认证提供者(pre-authenticated provider)的主要工作只是为用户加载 UserDetails 对象。 它通过委托给一个 AuthenticationUserDetailsService 来实现这一点。 后者与标准的 UserDetailsService 类似,但接收的是一个 Authentication 对象,而不仅仅是用户名:spring-doc.cadn.net.cn

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

该接口可能还有其他用途,但在预认证(pre-authentication)场景下,它允许访问上一节中我们所看到的、封装在 Authentication 对象中的权限信息。 PreAuthenticatedGrantedAuthoritiesUserDetailsService 类正是用于此目的。 或者,它也可以通过 UserDetailsService 实现类委托给一个标准的 UserDetailsByNameServiceWrapperspring-doc.cadn.net.cn

HTTP 403 禁止访问入口点

AuthenticationEntryPoint 负责为未认证用户(当他们尝试访问受保护资源时)启动认证流程。然而,在预认证的情况下,此规则不适用。 仅当您不使用预认证与其他认证机制结合时,才需要将 ExceptionTranslationFilter 配置为此类的实例。 如果用户被 AbstractPreAuthenticatedProcessingFilter 拒绝并导致认证结果为 null,则会调用它。 如果被调用,它始终返回 403-禁止响应码。spring-doc.cadn.net.cn

具体实现

X.509 认证已在其独立章节中介绍。 此处,我们将探讨一些为其他预认证场景提供支持的类。spring-doc.cadn.net.cn

请求头认证 (Siteminder)

外部认证系统可以通过在 HTTP 请求中设置特定的头部(header)向应用程序提供信息。 一个广为人知的例子是 Siteminder,它通过名为 SM_USER 的头部传递用户名。 该机制由 RequestHeaderAuthenticationFilter 类支持,该类仅从头部中提取用户名。 默认情况下,它使用 SM_USER 作为头部名称。 更多详细信息,请参阅 Javadoc。spring-doc.cadn.net.cn

使用此类系统时,框架完全不会执行任何身份验证检查,因此极其重要的是,外部系统必须正确配置,并保护对应用程序的所有访问。 如果攻击者能够在不被检测到的情况下伪造其原始请求中的标头,他们就可能随意选择任意用户名。spring-doc.cadn.net.cn

Siteminder 示例配置

以下示例展示了一个使用此过滤器的典型配置:spring-doc.cadn.net.cn

<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”),用于加载用户的角色。spring-doc.cadn.net.cn

Java EE 容器认证

J2eePreAuthenticatedProcessingFilter 类从 userPrincipalHttpServletRequest 属性中提取用户名。 通常,该过滤器的使用会与 Java EE 角色结合,如前面在 J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource 中所述。spring-doc.cadn.net.cn

代码库中有一个示例应用程序使用了这种方法,因此如果你感兴趣,可以从 Github 获取代码并查看应用程序上下文文件。spring-doc.cadn.net.cn