|
此版本仍在开发中,尚未被视为稳定版本。如需最新稳定版本,请使用 Spring Security 7.0.4! |
预认证场景
示例包括 X.509、Siteminder,以及应用程序运行所在的 Java EE 容器进行的身份验证。 使用预认证时,Spring Security 必须:
-
识别发起请求的用户。
-
获取用户的权限。
具体细节取决于外部认证机制。
在使用 X.509 的情况下,用户可能通过其证书信息进行识别;而在使用 Siteminder 的情况下,则可能通过 HTTP 请求头进行识别。
如果依赖容器认证,则通过调用传入 HTTP 请求上的 getUserPrincipal() 方法来识别用户。
在某些情况下,外部机制可能会为用户提供角色和权限信息。但在其他情况下,您必须从单独的来源(例如 UserDetailsService)获取权限信息。
预认证框架类
由于大多数预认证(pre-authentication)机制遵循相同的模式,Spring Security 提供了一组类,用于构建实现预认证身份验证提供程序的内部框架。
这消除了重复代码,并允许以结构化的方式添加新的实现,而无需从头开始编写所有内容。
如果您只想使用类似 X.509 认证 的功能,则无需了解这些类,因为它已经提供了命名空间配置选项,使用起来更简单,也更容易上手。
但如果您需要使用显式的 Bean 配置,或者计划编写自己的实现,就需要理解所提供的实现是如何工作的。
这些类位于 org.springframework.security.web.authentication.preauth 包下。
我们在此仅提供一个概要,因此在适当的情况下,您应查阅相关的 Javadoc 和源代码。
抽象预认证过滤器
该类检查安全上下文的当前内容,如果为空,则尝试从 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 对象,用于在 details 对象的 Authentication 属性中存储额外信息,例如会话标识符和原始 IP 地址。
在某些情况下,如果用户角色信息可以从预认证机制中获取,这些数据也会存储在此属性中,且详情对象会实现 GrantedAuthoritiesContainer 接口。
这使得认证提供者能够读取外部为用户分配的权限。
接下来,我们将查看一个具体的示例。
基于 J2EE 的预认证 Web 认证详情源
如果该过滤器配置了一个 authenticationDetailsSource(即此类的一个实例),则会针对一组预定义的“可映射角色”(mappable roles)逐一调用 isUserInRole(String role) 方法来获取权限信息。
该类通过一个已配置的 MappableAttributesRetriever 获取这些角色。
可能的实现方式包括在应用程序上下文中硬编码一个角色列表,或者从 <security-role> 文件中的 web.xml 信息中读取角色数据。
预认证(pre-authentication)示例应用程序采用的是后一种方法。
还有一个额外的阶段,即通过配置的 GrantedAuthority 将角色(或属性)映射为 Spring Security 的 Attributes2GrantedAuthoritiesMapper 对象。
默认情况下,它只是在名称前添加常规的 ROLE_ 前缀,但同时也让你能够完全控制该行为。
预认证身份验证提供者
预认证提供者(pre-authenticated provider)的主要工作只是为用户加载 UserDetails 对象。
它通过委托给一个 AuthenticationUserDetailsService 来实现这一点。
后者与标准的 UserDetailsService 类似,但接收的是一个 Authentication 对象,而不仅仅是用户名:
public interface AuthenticationUserDetailsService {
UserDetails loadUserDetails(Authentication token) throws UsernameNotFoundException;
}
该接口可能还有其他用途,但在预认证(pre-authentication)场景下,它允许访问上一节中我们所看到的、封装在 Authentication 对象中的权限信息。
PreAuthenticatedGrantedAuthoritiesUserDetailsService 类正是用于此目的。
或者,它也可以通过 UserDetailsService 实现类委托给一个标准的 UserDetailsByNameServiceWrapper。
HTTP 403 禁止访问入口点
AuthenticationEntryPoint 负责为未认证用户(当他们尝试访问受保护资源时)启动认证流程。然而,在预认证的情况下,此规则不适用。
仅当您不使用预认证与其他认证机制结合时,才需要将 ExceptionTranslationFilter 配置为此类的实例。
如果用户被 AbstractPreAuthenticatedProcessingFilter 拒绝并导致认证结果为 null,则会调用它。
如果被调用,它始终返回 403-禁止响应码。
具体实现
X.509 认证已在其独立章节中介绍。 此处,我们将探讨一些为其他预认证场景提供支持的类。
请求头认证 (Siteminder)
外部认证系统可以通过在 HTTP 请求中设置特定的头部(header)向应用程序提供信息。
一个广为人知的例子是 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 类从 userPrincipal 的 HttpServletRequest 属性中提取用户名。
通常,该过滤器的使用会与 Java EE 角色结合,如前面在 J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource 中所述。
代码库中有一个示例应用程序使用了这种方法,因此如果你感兴趣,可以从 Github 获取代码并查看应用程序上下文文件。