预认证场景
本站(springdoc.cn)中的内容来源于 spring.io ,原始版权归属于 spring.io。由 springdoc.cn 进行翻译,整理。可供个人学习、研究,未经许可,不得进行任何转载、商用或与之相关的行为。 商标声明:Spring 是 Pivotal Software, Inc. 在美国以及其他国家的商标。 |
有些情况下,你想使用Spring Security进行授权,但用户在访问应用程序之前已经被一些外部系统可靠地验证过了。我们把这些情况称为 “pre-authenticated” (预认证)场景。例子包括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
这个类检查 security context 的当前内容,如果它是空的,则试图从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
,并将其提交给认证。这里的 “authentication”(认证),我们实际上只是指进一步的处理,也许是加载用户的权限,但标准的 Spring Security 认证架构被遵循。
与其他 Spring Security 认证过滤器一样,预认证过滤器有一个 authenticationDetailsSource
属性,默认情况下,它创建一个 WebAuthenticationDetails
对象来存储额外的信息,例如会话标识符和 Authentication
对象的 details
信息属性中的源IP地址。在可以从预认证机制中获得用户角色信息的情况下,该数据也被存储在该属性中,其细节实现了 GrantedAuthoritiesContainer
接口。这使得认证提供者能够读取从外部分配给用户的授权。我们接下来看一个具体的例子。
J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource
如果过滤器被配置为 authenticationDetailsSource
,也就是这个类的实例,权限信息是通过调用 isUserInRole(String role)
方法获得的,每个预先确定的 “mappable roles” (可应用角色)。
该类从配置好的 MappableAttributesRetriever
获得这些信息。
可能的实现包括在应用程序上下文中硬编码一个列表,以及从 web.xml
文件中的 <security-role>
信息读取角色信息。
预认证示例应用程序使用后一种方法。
还有一个额外的阶段,通过使用配置的 Attributes2GrantedAuthoritiesMapper
将角色(或属性)映射到 Spring Security GrantedAuthority
对象。默认情况下,只是将通常的 ROLLE_
前缀添加到名称中,但它给了你对行为的完全控制。
PreAuthenticatedAuthenticationProvider
预先认证的提供者除了为用户加载 UserDetails
对象外,几乎没有其他事情可做。它通过委托给一个 AuthenticationUserDetailsService
来完成这个任务。后者类似于标准的 UserDetailsService
,但需要一个 Authentication
对象,而不仅仅是用户名。
public interface AuthenticationUserDetailsService {
UserDetails loadUserDetails(Authentication token) throws UsernameNotFoundException;
}
这个接口也可能有其他用途,但是,通过预认证,它允许访问被打包在 Authentication
对象中的授权,就像我们在上一节看到的那样。PreAuthenticatedGrantedAuthoritiesUserDetailsService
类就是这样做的。或者,它可以通过 UserDetailsByNameServiceWrapper
实现委托给一个标准的 UserDetailsService
。
Http403ForbiddenEntryPoint
AuthenticationEntryPoint
负责为未认证的用户启动认证过程(当他们试图访问受保护的资源时)。然而,在预先认证的情况下,这并不适用。如果你不使用预认证与其他认证机制相结合,你只能用这个类的实例来配置 ExceptionTranslationFilter
。如果用户被 AbstractPreAuthenticatedProcessingFilter
拒绝,导致认证无效,它就被调用。如果被调用,它总是返回一个 403
-forbidden 响应代码。
具体的实施方案
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>
我们在这里假设 security namespace 被用于配置。还假设你在配置中添加了一个 UserDetailsService
(称为 userDetailsService
)来加载用户的角色。
Java EE 容器认证
J2eePreAuthenticatedProcessingFilter
类从 HttpServletRequest
的 userPrincipal
属性提取用户名。这个过滤器的使用通常会与 Java EE 角色的使用相结合,如前面 J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource 中所述。
在代码库中,有一个使用这种方法的 示例应用程序,如果你有兴趣,可以从Github上获取代码,并看一下 application context 文件。