Pre-Authentication Scenarios

示例包括 X.509、Siteminder 和应用程序运行所在的 Java EE 容器的身份验证。在使用预身份验证时,Spring Security 必须:

Examples include X.509, Siteminder, and authentication by the Java EE container in which the application runs. When using pre-authentication, Spring Security has to:

  • Identify the user making the request.

  • Obtain the authorities for the user.

详细信息取决于外部身份验证机制。在 X.509 的情况下,可以通过用户证书信息来标识用户,或在 Siteminder 的情况下,可以通过 HTTP 请求头来标识用户。如果依赖容器身份验证,则通过调用传入的 HTTP 请求上的 getUserPrincipal() 方法来标识用户。在某些情况下,外部机制可能会为用户提供角色和权限信息。但是,在其他情况下,您必须从单独的来源(如 UserDetailsService )获取权限。

The details depend on the external authentication mechanism. A user might be identified by their certificate information in the case of X.509, or by an HTTP request header in the case of Siteminder. If relying on container authentication, the user is identified by calling the getUserPrincipal() method on the incoming HTTP request. In some cases, the external mechanism may supply role and authority information for the user. However, in other cases, you must obtain the authorities from a separate source, such as a UserDetailsService.

Pre-Authentication Framework Classes

由于大多数预身份验证机制遵循相同的模式,因此 Spring Security 有一组类,它们提供了一个内部框架用于实现预身份验证验证提供程序。这样消除了重复,并且可以以结构化的方式添加新实现,而无需从头开始编写所有内容。如果你想要使用 X.509 authentication 之类的方法,则无需了解这些类,因为它已经有了更易于使用和上手的命名空间配置选项。如果你需要使用显式 Bean 配置,或者计划编写自己的实现,则需要了解提供的实现是如何工作的。你可以在 org.springframework.security.web.authentication.preauth 下找到这些类。我们在此只提供一个概要,因此你应在适当的情况下查阅 Javadoc 和源代码。

Because most pre-authentication mechanisms follow the same pattern, Spring Security has a set of classes that provide an internal framework for implementing pre-authenticated authentication providers. This removes duplication and lets new implementations be added in a structured fashion, without having to write everything from scratch. You need not know about these classes if you want to use something like X.509 authentication, as it already has a namespace configuration option which is simpler to use and get started with. If you need to use explicit bean configuration or are planning on writing your own implementation, you need an understanding of how the provided implementations work. You can find the classes under the org.springframework.security.web.authentication.preauth. We provide only an outline here, so you should consult the Javadoc and source where appropriate.

AbstractPreAuthenticatedProcessingFilter

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

This class checks the current contents of the security context and, if it is empty, tries to extract user information from the HTTP request and submit it to the AuthenticationManager. Subclasses override the following methods to obtain this information.

Override 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 ,并将其提交进行身份验证。在"`authentication`"中,我们的真实意思是进一步处理,以加载用户的权限,但遵循标准的 Spring Security 身份验证架构。

After calling these, the filter creates a PreAuthenticatedAuthenticationToken that contains the returned data and submits it for authentication. By “authentication” here, we really just mean further processing to perhaps load the user’s authorities, but the standard Spring Security authentication architecture is followed.

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

As other Spring Security authentication filters, the pre-authentication filter has an authenticationDetailsSource property, which, by default, creates a WebAuthenticationDetails object to store additional information, such as the session identifier and the originating IP address in the details property of the Authentication object. In cases where user role information can be obtained from the pre-authentication mechanism, the data is also stored in this property, with the details implementing the GrantedAuthoritiesContainer interface. This enables the authentication provider to read the authorities which were externally allocated to the user. We look at a concrete example next.

J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource

如果过滤器配置了此类的实例 authenticationDetailsSource ,则通过为一组预先确定的 “mappable roles” 中的每一个调用 isUserInRole(String role) 方法来获取权限信息。该类从配置的 MappableAttributesRetriever 中获取这些信息。可能的实现包括对应用程序上下文中硬编码一个列表,以及从 <security-role> 文件的 web.xml 信息中读取角色信息。预身份验证示例应用程序使用后一种方法。

If the filter is configured with an authenticationDetailsSource, which is an instance of this class, the authority information is obtained by calling the isUserInRole(String role) method for each of a pre-determined set of “mappable roles”. The class gets these from a configured MappableAttributesRetriever. Possible implementations include hard-coding a list in the application context and reading the role information from the <security-role> information in a web.xml file. The pre-authentication sample application uses the latter approach.

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

There is an additional stage where the roles (or attributes) are mapped to Spring Security GrantedAuthority objects by using a configured Attributes2GrantedAuthoritiesMapper. The default just adds the usual ROLE_ prefix to the names, but it gives you full control over the behavior.

PreAuthenticatedAuthenticationProvider

预身份验证提供程序除了为用户加载 UserDetails 对象外,几乎没有任何其他工作要做。它通过委派给 AuthenticationUserDetailsService 来完成此操作。后者类似于标准的 UserDetailsService ,但使用 Authentication 对象,而不仅仅是用户名:

The pre-authenticated provider has little more to do than load the UserDetails object for the user. It does this by delegating to an AuthenticationUserDetailsService. The latter is similar to the standard UserDetailsService but takes an Authentication object rather than just user name:

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

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

This interface may also have other uses, but, with pre-authentication, it allows access to the authorities that were packaged in the Authentication object, as we saw in the previous section. The PreAuthenticatedGrantedAuthoritiesUserDetailsService class does this. Alternatively, it may delegate to a standard UserDetailsService through the UserDetailsByNameServiceWrapper implementation.

Http403ForbiddenEntryPoint

AuthenticationEntryPoint 负责为未验证用户启动身份验证过程(当他们尝试访问受保护的资源时)。但是,在预身份验证的情况下,这并不适用。如果你不结合其他身份验证机制来使用预身份验证,则只需使用此类的实例配置 ExceptionTranslationFilter。如果 AbstractPreAuthenticatedProcessingFilter 拒绝用户,则调用该方法,从而导致 null 身份验证。如果调用,它总是返回 403- 禁止的响应代码。

The AuthenticationEntryPoint is responsible for kick-starting the authentication process for an unauthenticated user (when they try to access a protected resource). However, in the pre-authenticated case, this does not apply. You would only configure the ExceptionTranslationFilter with an instance of this class if you do not use pre-authentication in combination with other authentication mechanisms. It is called if the user is rejected by the AbstractPreAuthenticatedProcessingFilter, resulting in a null authentication. It always returns a 403-forbidden response code if called.

Concrete Implementations

X.509 身份验证在其 own chapter 中进行了介绍。在这里,我们来看一些为其他预身份验证场景提供支持的类。

X.509 authentication is covered in its own chapter. Here, we look at some classes which provide support for other pre-authenticated scenarios.

Request-Header Authentication (Siteminder)

外部身份验证系统可以通过在 HTTP 请求中设置特定标题来向应用程序提供信息。一个众所周知的示例是 Siteminder,它在一个名为 SM_USER 的标题中传递用户名。此机制受 RequestHeaderAuthenticationFilter 类的支持,它只从标题中提取用户名。它默认为将标题名称用作 SM_USER 名称。有关更多详细信息,请参阅 Javadoc。

An external authentication system may supply information to the application by setting specific headers on the HTTP request. A well-known example of this is Siteminder, which passes the username in a header called SM_USER. This mechanism is supported by the RequestHeaderAuthenticationFilter class, which only extracts the username from the header. It defaults to using a name of SM_USER as the header name. See the Javadoc for more details.

使用这样的系统时,框架根本不执行身份验证检查,并且 extremely 重要的是外部系统已正确配置并保护对应用程序的所有访问。如果攻击者能够在不被检测到的情况下伪造其原始请求中的标题,则他们可能会选择他们想要的任何用户名。

When using a system like this, the framework performs no authentication checks at all, and it is extremely important that the external system is configured properly and protects all access to the application. If an attacker is able to forge the headers in their original request without this being detected, they could potentially choose any username they wished.

Siteminder Example Configuration

以下示例显示了使用此过滤器的典型配置:

The following example shows a typical configuration that uses this filter:

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

我们在此假定正在为配置使用 security namespace。还假定你已将 UserDetailsService(称为“userDetailsService”)添加到你的配置中以加载用户角色。

We’ve assumed here that the security namespace is being used for configuration. It’s also assumed that you have added a UserDetailsService (called "userDetailsService") to your configuration to load the user’s roles.

Java EE Container Authentication

J2eePreAuthenticatedProcessingFilter 类从 HttpServletRequestuserPrincipal 属性中提取用户名。该过滤器的使用通常与 Java EE 角色结合使用(如先前的 J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource 中所述)。

The J2eePreAuthenticatedProcessingFilter class extracts the username from the userPrincipal property of the HttpServletRequest. Use of this filter would usually be combined with the use of Java EE roles, as described earlier in J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.

有很多 示例应用程序 在代码库中使用了这种方法,因此如果你感兴趣,可以从 Github 中获取代码并查看应用程序上下文文件。

There is a sample application that uses this approach in the codebase, so get hold of the code from Github and have a look at the application context file if you are interested.