运行身份认证替换
AbstractSecurityInterceptor 能够在安全对象回调阶段临时替换 SecurityContext 和 SecurityContextHolder 中的 Authentication 对象。这种情况仅发生在原始 Authentication 对象被 AuthenticationManager 和 AccessDecisionManager 成功处理后。RunAsManager 会指示在 SecurityInterceptorCallback 期间应使用的替换 Authentication 对象(如果存在)。
通过在安全对象回调阶段临时替换Authentication对象,安全调用可以调用需要不同身份验证和授权凭据的其他对象。它还可以针对特定的GrantedAuthority对象执行任何内部安全检查。由于 Spring Security 提供了许多辅助类,能够基于SecurityContextHolder的内容自动配置远程协议,因此在调用远程 Web 服务时,这些运行身份替换功能尤为有用。
配置
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 的授予权限。
替换后的 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在创建后是不可变的。