Java 认证与授权服务 (JAAS) 提供者
Spring Security 提供了一个包,用于将身份验证请求委托给 Java 认证与授权服务(JAAS)。本节将讨论该包。
AbstractJaasAuthenticationProvider
AbstractJaasAuthenticationProvider 类是所提供的 JAAS AuthenticationProvider 实现的基础。子类必须实现一个创建 LoginContext 的方法。AbstractJaasAuthenticationProvider 具有许多可以注入其中的依赖项,正如本节剩余部分所讨论的那样。
JAAS 回调处理器
大多数JAAS LoginModule实例都需要某种回调。这些回调通常用于从用户处获取用户名和密码。
在Spring Security部署中,Spring Security负责处理用户交互(通过认证机制)。因此,当认证请求被委托给JAAS处理时,Spring Security的认证机制已经完整地填充了一个Authentication对象,该对象包含了JAAS LoginModule所需的所有信息。
因此,Spring Security 的 JAAS 包提供了两个默认的回调处理器:JaasNameCallbackHandler 和 JaasPasswordCallbackHandler。这两个回调处理器都实现了 JaasAuthenticationCallbackHandler 接口。在大多数情况下,无需了解其内部机制即可使用这些回调处理器。
对于需要完全控制回调行为的用户,AbstractJaasAuthenticationProvider 会在内部使用 InternalCallbackHandler 包装这些 JaasAuthenticationCallbackHandler 实例。InternalCallbackHandler 是实际实现 JAAS 标准 CallbackHandler 接口的类。每当使用 JAAS LoginModule 时,都会向其传递一个由应用上下文配置的 InternalCallbackHandler 实例列表。如果 LoginModule 向 InternalCallbackHandler 实例请求回调,该回调会进一步传递给被包装的 JaasAuthenticationCallbackHandler 实例。
JAAS AuthorityGranter
JAAS 基于主体(principals)工作,即使是“角色”在JAAS中也被表示为主体。而Spring Security则基于Authentication对象工作。每个Authentication对象包含一个主体和多个GrantedAuthority实例。为了便于在这些不同概念之间进行映射,Spring Security的JAAS包包含了一个AuthorityGranter接口。
AuthorityGranter 负责检查 JAAS 主体并返回一组表示分配给该主体的权限的 String 对象。对于每个返回的权限字符串,AbstractJaasAuthenticationProvider 会创建一个 JaasGrantedAuthority(它实现了 Spring Security 的 GrantedAuthority 接口),其中包含权限字符串以及传递给 AuthorityGranter 的 JAAS 主体。AbstractJaasAuthenticationProvider 通过首先使用 JAAS LoginModule 成功验证用户凭据,然后访问其返回的 LoginContext 来获取 JAAS 主体。它会调用 LoginContext.getSubject().getPrincipals(),并将每个结果主体传递给针对 AbstractJaasAuthenticationProvider.setAuthorityGranters(List) 属性定义的每个 AuthorityGranter。
Spring Security 不包含任何生产环境可用的 AuthorityGranter 实例,因为每个 JAAS 主体都具有特定于实现的意义。然而,当认证成功时,Spring Security 默认会颁发 FACTOR_PASSWORD 权限。
单元测试中有一个 TestAuthorityGranter,它演示了一个简单的 AuthorityGranter 实现。
DefaultJaasAuthenticationProvider
DefaultJaasAuthenticationProvider 允许将 JAAS Configuration 对象作为依赖项注入其中。随后,它通过使用注入的 JAAS Configuration 来创建 LoginContext。这意味着 DefaultJaasAuthenticationProvider 并不像 JaasAuthenticationProvider 那样绑定到任何特定的 Configuration 实现。
InMemoryConfiguration
为便于将 Configuration 注入 DefaultJaasAuthenticationProvider,框架提供了一个默认的内存实现 InMemoryConfiguration。该实现的构造函数接收一个 Map 参数,其中每个键代表一个登录配置名称,对应的值是一个 AppConfigurationEntry 实例的 Array。InMemoryConfiguration 还支持一个默认的 AppConfigurationEntry 对象 Array,当提供的 Map 中未找到映射时会使用此默认配置。具体细节请参阅 InMemoryConfiguration 的 Javadoc。
DefaultJaasAuthenticationProvider 示例配置
虽然 InMemoryConfiguration 的 Spring 配置可能比标准的 JAAS 配置文件更为冗长,但将其与 DefaultJaasAuthenticationProvider 结合使用,比 JaasAuthenticationProvider 更为灵活,因为它不依赖于默认的 Configuration 实现。
下一个示例展示了使用 InMemoryConfiguration 配置 DefaultJaasAuthenticationProvider 的方法。请注意,自定义的 Configuration 实现也可以轻松注入到 DefaultJaasAuthenticationProvider 中。
<bean id="jaasAuthProvider"
class="org.springframework.security.authentication.jaas.DefaultJaasAuthenticationProvider">
<property name="configuration">
<bean class="org.springframework.security.authentication.jaas.memory.InMemoryConfiguration">
<constructor-arg>
<map>
<!--
SPRINGSECURITY is the default loginContextName
for AbstractJaasAuthenticationProvider
-->
<entry key="SPRINGSECURITY">
<array>
<bean class="javax.security.auth.login.AppConfigurationEntry">
<constructor-arg value="sample.SampleLoginModule" />
<constructor-arg>
<util:constant static-field=
"javax.security.auth.login.AppConfigurationEntry$LoginModuleControlFlag.REQUIRED"/>
</constructor-arg>
<constructor-arg>
<map></map>
</constructor-arg>
</bean>
</array>
</entry>
</map>
</constructor-arg>
</bean>
</property>
<property name="authorityGranters">
<list>
<!-- You will need to write your own implementation of AuthorityGranter -->
<bean class="org.springframework.security.authentication.jaas.TestAuthorityGranter"/>
</list>
</property>
</bean>
JaasAuthenticationProvider
JaasAuthenticationProvider 假定默认的 Configuration 是 ConfigFile 的一个实例。做出此假设是为了尝试更新 Configuration。随后,JaasAuthenticationProvider 使用默认的 Configuration 来创建 LoginContext。
假设我们有一个JAAS登录配置文件 /WEB-INF/login.conf,其内容如下:
JAASTest {
sample.SampleLoginModule required;
};
与所有Spring Security bean一样,JaasAuthenticationProvider 通过应用上下文进行配置。以下定义将对应上述JAAS登录配置文件:
<bean id="jaasAuthenticationProvider"
class="org.springframework.security.authentication.jaas.JaasAuthenticationProvider">
<property name="loginConfig" value="/WEB-INF/login.conf"/>
<property name="loginContextName" value="JAASTest"/>
<property name="callbackHandlers">
<list>
<bean
class="org.springframework.security.authentication.jaas.JaasNameCallbackHandler"/>
<bean
class="org.springframework.security.authentication.jaas.JaasPasswordCallbackHandler"/>
</list>
</property>
<property name="authorityGranters">
<list>
<bean class="org.springframework.security.authentication.jaas.TestAuthorityGranter"/>
</list>
</property>
</bean>
以主题身份运行
如果已配置,JaasApiIntegrationFilter 会尝试以 JaasAuthenticationToken 上的 Subject 身份运行。这意味着可以通过以下方式访问 Subject:
Subject subject = Subject.getSubject(AccessController.getContext());
您可以通过使用 jaas-api-provision 属性来配置此集成。此功能在与依赖 JAAS Subject 被填充的遗留或外部 API 集成时非常有用。