Run-As 认证替换

`AbstractSecurityInterceptor` 能够在安全对象回调阶段临时替换 `SecurityContext` 和 `SecurityContextHolder` 中的 `Authentication` 对象。这只有在原始 `Authentication` 对象被 `AuthenticationManager` 和 `AccessDecisionManager` 成功处理后才会发生。`RunAsManager` 指示在 `SecurityInterceptorCallback` 期间应使用的替换 `Authentication` 对象(如果存在)。

通过在安全对象回调阶段临时替换 `Authentication` 对象,安全调用可以调用需要不同认证和授权凭据的其他对象。它还可以对特定的 `GrantedAuthority` 对象执行任何内部安全检查。由于 Spring Security 提供了许多帮助类,可以根据 `SecurityContextHolder` 的内容自动配置远程协议,因此在调用远程 Web 服务时,这些 run-as 替换尤其有用。

配置

Spring Security 提供了 `RunAsManager` 接口

Authentication buildRunAs(Authentication authentication, Object object,
	List<ConfigAttribute> config);

boolean supports(ConfigAttribute attribute);

boolean supports(Class clazz);

第一个方法返回在方法调用期间应替换现有 `Authentication` 对象的 `Authentication` 对象。如果方法返回 `null`,则表示不应进行替换。第二个方法由 `AbstractSecurityInterceptor` 用于启动时配置属性的验证。安全拦截器实现会调用 `supports(Class)` 方法,以确保配置的 `RunAsManager` 支持安全拦截器呈现的安全对象的类型。

Spring Security 提供了一个具体的 `RunAsManager` 实现。`RunAsManagerImpl` 类在任何 `ConfigAttribute` 以 `RUN_AS_` 开头时,都会返回一个替换的 `RunAsUserToken`。如果找到任何此类 `ConfigAttribute`,替换的 `RunAsUserToken` 将包含与原始 `Authentication` 对象相同的 principal、credentials 和 granted authorities,以及每个 `RUN_AS_` `ConfigAttribute` 对应的一个新的 `SimpleGrantedAuthority`。每个新的 `SimpleGrantedAuthority` 都以 `ROLE_` 为前缀,后跟 `RUN_AS` `ConfigAttribute`。例如,`RUN_AS_SERVER` 会导致替换的 `RunAsUserToken` 包含一个 `ROLE_RUN_AS_SERVER` granted authority。

替换的 `RunAsUserToken` 与任何其他 `Authentication` 对象一样。它需要由 `AuthenticationManager` 进行认证,这通常是通过委托给合适的 `AuthenticationProvider` 来完成的。`RunAsImplAuthenticationProvider` 执行此类认证。它接受任何提供的 `RunAsUserToken` 为有效。

为了确保恶意代码不会创建 `RunAsUserToken` 并提交给 `RunAsImplAuthenticationProvider` 以保证被接受,所有生成的令牌中都存储了一个密钥的哈希值。`RunAsManagerImpl` 和 `RunAsImplAuthenticationProvider` 在 bean 上下文中创建时使用相同的密钥

<bean id="runAsManager"
	class="org.springframework.security.access.intercept.RunAsManagerImpl">
<property name="key" value="my_run_as_password"/>
</bean>

<bean id="runAsAuthenticationProvider"
	class="org.springframework.security.access.intercept.RunAsImplAuthenticationProvider">
<property name="key" value="my_run_as_password"/>
</bean>

通过使用相同的密钥,每个 `RunAsUserToken` 都可以被验证,因为它是由经批准的 `RunAsManagerImpl` 创建的。出于安全原因,`RunAsUserToken` 在创建后是不可变的。