# 预认证场景

示例包括 X.509、SiteMinder 和通过运行应用程序的 爪哇 EE 容器进行的身份验证。在使用预身份验证时， Spring 安全性必须

* 识别提出请求的用户。

* 为用户获取权限。

细节将取决于外部身份验证机制。在 X.509 的情况下，用户可以通过他们的证书信息来标识，在 SiteMinder 的情况下，用户可以通过 HTTP 请求头来标识。如果依赖于容器身份验证，则将通过在传入的 HTTP 请求上调用`getUserPrincipal()`方法来识别用户。在某些情况下，外部机制可以为用户提供角色/权限信息，但在另一些情况下，权限必须从单独的来源获得，例如`UserDetailsService`。

## 预认证框架类

因为大多数预身份验证机制遵循相同的模式， Spring Security 有一组类，它们为实现预身份验证的身份验证提供者提供了一个内部框架。这消除了重复，并允许以结构化的方式添加新的实现，而无需从头开始编写所有内容。如果你想使用[X.509 认证](x509.html#servlet-x509)之类的东西，那么你不需要了解这些类，因为它已经有了一个名称空间配置选项，该选项更易于使用和启动。如果你需要使用显式 Bean 配置，或者正在计划编写自己的实现，那么了解所提供的实现是如何工作的将是有用的。你将在`org.springframework.security.web.authentication.preauth`下找到类。我们只是在这里提供一个大纲，所以你应该在适当的地方参考 Javadoc 和源代码。

### 抽象预先验证的处理过滤器

这个类将检查安全上下文的当前内容，如果为空，它将尝试从 HTTP 请求中提取用户信息，并将其提交给`AuthenticationManager`。子类覆盖以下方法以获得此信息：

例 1。覆盖抽象预认证处理过滤器

Java

```
protected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest request);

protected abstract Object getPreAuthenticatedCredentials(HttpServletRequest request);
```

Kotlin

```
protected abstract fun getPreAuthenticatedPrincipal(request: HttpServletRequest): Any?

protected abstract fun getPreAuthenticatedCredentials(request: HttpServletRequest): Any?
```

在调用这些之后，筛选器将创建一个`PreAuthenticatedAuthenticationToken`，其中包含返回的数据，并将其提交以进行身份验证。这里的“身份验证”实际上只是指进一步的处理，以便可能加载用户的权限，但是遵循了标准的安全身份验证体系结构 Spring。

与其他 Spring 安全身份验证过滤器一样，预身份验证过滤器具有`authenticationDetailsSource`属性，该属性在默认情况下将创建`WebAuthenticationDetails`对象，以在`Authentication`对象的`details`属性中存储诸如会话-标识符和发起 IP 地址等附加信息。在可以从预身份验证机制获得用户角色信息的情况下，该数据也存储在该属性中，具有实现`GrantedAuthoritiesContainer`接口的细节。这使身份验证提供程序能够读取外部分配给用户的权限。接下来我们将看一个具体的例子。

#### J2EebasedPreAuthenticatedWebAuthenticationDetailsSource

如果筛选器配置了`authenticationDetailsSource`，这是该类的一个实例，则通过为每个预先确定的“可映射角色”集合调用`isUserInRole(String role)`方法来获得权限信息。该类从配置的`MappableAttributesRetriever`获得这些。可能的实现方式包括在应用程序上下文中对列表进行硬编码，并从`<security-role>`文件中的`web.xml`信息中读取角色信息。预认证示例应用程序使用后一种方法。

还有一个额外的阶段，其中角色（或属性）使用配置的`Attributes2GrantedAuthoritiesMapper`映射到 Spring Security`GrantedAuthority`对象。默认值只会将通常的`ROLE_`前缀添加到名称中，但它使你可以完全控制行为。

### 预先验证 dauthenticationProvider

预先验证的提供者除了为用户加载`UserDetails`对象外，几乎没有什么可做的。它通过委托给`AuthenticationUserDetailsService`来实现这一点。后者类似于标准`UserDetailsService`，但接受一个`Authentication`对象，而不仅仅是用户名：

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

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

### HTTP403 禁止 BiddenentryPoint

[`AuthenticationEntryPoint`]（architecture.html# Servlet-authentication-authentryPoint）负责启动未经身份验证的用户的身份验证过程（当他们试图访问受保护的资源时），但在预先验证的情况下，这不适用。如果你 AREN 不将预身份验证与其他身份验证机制结合使用，那么你将仅使用该类的实例来配置`ExceptionTranslationFilter`。如果用户被`AbstractPreAuthenticatedProcessingFilter`拒绝，导致空身份验证，则将调用它。如果调用它，它总是返回`403`-禁止的响应代码。

## 具体实现

X.509 身份验证包含在其[自己的章节](x509.html#servlet-x509)中。在这里，我们将介绍一些类，它们为其他预先验证的场景提供了支持。

### 

外部身份验证系统可以通过在 HTTP 请求上设置特定的头来向应用程序提供信息。一个著名的例子是 SiteMinder，它在一个名为`SM_USER`的头文件中传递用户名。这个机制得到了`RequestHeaderAuthenticationFilter`类的支持，它只是从头部提取用户名。默认情况下，它使用`SM_USER`作为标题名称。有关更多详细信息，请参见 Javadoc。

|   |注意，当使用这样的系统时，框架根本不执行任何身份验证检查，它是*极端*重要的外部系统已正确配置，并保护了对应用程序的所有访问。<br/>如果攻击者能够伪造其原始请求中的头而不被检测到，那么他们可能会选择他们想要的任何用户名。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

#### 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>
```

这里我们假设[安全命名空间](../configuration/xml-namespace.html#ns-config)用于配置。还假定你在配置中添加了`UserDetailsService`（称为“UserDetailsService”）以加载用户的角色。

### Java EE 容器身份验证

类`J2eePreAuthenticatedProcessingFilter`将从`HttpServletRequest`的`userPrincipal`属性中提取用户名。这个过滤器的使用通常与上面[J2EebasedPreAuthenticatedWebAuthenticationDetailsSource ](#j2ee-preauth-details)中描述的 Java EE 角色的使用相结合。

在 Samples 项目中有一个[示例应用程序](https://github.com/spring-projects/spring-security/tree/5.4.x/samples/xml/preauth)使用这种方法，因此，如果你感兴趣的话，可以从 Github 获取代码并查看应用程序上下文文件。

[Anonymous](anonymous.html)[JAAS](jaas.html)

