Java Authentication and Authorization Service (JAAS) Provider

Spring Security 提供了一个将身份验证请求委托给 Java 身份验证和授权服务 (JAAS) 的包。本节将对该包进行讨论。

AbstractJaasAuthenticationProvider

AbstractJaasAuthenticationProvider 类是所提供的 JAAS AuthenticationProvider 实现的基础。子类必须实现一个创建 LoginContext 的方法。AbstractJaasAuthenticationProvider 具有多个依赖项,可以通过注入来处理,如本节余下部分中所述。

JAAS CallbackHandler

大多数 JAAS LoginModule 实例需要某种回调。这些回调通常用于从用户那里获取用户名和密码。

在 Spring Security 部署中,Spring Security 对此用户交互负责(通过身份验证机制)。因此,在身份验证请求通过 JAAS 委派时,Spring Security 的身份验证机制已经完全填充了一个 Authentication 对象,其中包含 JAAS LoginModule 所需的所有信息。

因此,针对 Spring Security 的 JAAS 包提供了两个默认回调处理程序: JaasNameCallbackHandlerJaasPasswordCallbackHandler。每个回调处理程序实现 JaasAuthenticationCallbackHandler。在大多数情况下,这些回调处理程序可以使用,而不需要理解内部机制。

对于需要完全控制回调行为的用户,AbstractJaasAuthenticationProvider 会使用 JaasAuthenticationCallbackHandler 实例对这些进行内部包装,然后使用 InternalCallbackHandlerInternalCallbackHandler 是真正实现 JAAS 正常 CallbackHandler 接口的类。每次 JAAS LoginModule 使用时,都会向它传递一个已配置 InternalCallbackHandler 实例的应用程序上下文列表。如果 LoginModule 请求针对 InternalCallbackHandler 实例进行回调,则该回调会依次传递给正在包装的 JaasAuthenticationCallbackHandler 实例。

JAAS AuthorityGranter

JAAS 使用主体。甚至 “roles” 也表示为 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 主体。按照 AbstractJaasAuthenticationProvider.setAuthorityGranters(List) 属性所定义的每个 AuthorityGranter,调用 LoginContext.getSubject().getPrincipals(),然后将每个结果主体传递给每个 AuthorityGranter

Spring Security 不包含任何生产 AuthorityGranter 实例,因为每个 JAAS 主体都有一个特定于实现的含义。但是,单元测试中有一个 TestAuthorityGranter,演示了一个简单的 AuthorityGranter 实现。

DefaultJaasAuthenticationProvider

DefaultJaasAuthenticationProvider 允许向它注入一个 JAAS Configuration 对象作为依赖项。然后它使用注入的 JAAS Configuration 创建一个 LoginContext。这意味着 DefaultJaasAuthenticationProvider 并没有诸如 JaasAuthenticationProvider 一样绑定到 Configuration 的任何特定实现。

InMemoryConfiguration

为了方便将 Configuration 注入到 DefaultJaasAuthenticationProvider 中,提供了名为 InMemoryConfiguration 的默认内存实现。实现构造函数接受一个 Map,其中每个键代表一个登录配置名称,而值代表 AppConfigurationEntry 实例的 ArrayInMemoryConfiguration 还支持一个 AppConfigurationEntry 对象的默认 Array,如果在所提供的 Map 中找不到任何映射,则将使用该对象。有关详细信息,请参阅 {security-api-url}org/springframework/security/authentication/jaas/memory/InMemoryConfiguration.html[InMemoryConfiguration 的 Javadoc]。

DefaultJaasAuthenticationProvider Example Configuration

虽然针对 InMemoryConfiguration 的 Spring 配置可能会比标准 JAAS 配置文件更加冗长,但将其与 DefaultJaasAuthenticationProvider 结合使用要比 JaasAuthenticationProvider 更加灵活,因为它不依赖于默认 Configuration 实现。

下一个示例提供了一个 DefaultJaasAuthenticationProvider 的配置,它使用 InMemoryConfiguration。请注意,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 假定默认 ConfigurationConfigFile 的一个实例。进行此假定是为了尝试更新 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>

Running as a Subject

如果已配置,JaasApiIntegrationFilter 尝试在 JaasAuthenticationToken 上以 Subject 身份运行。这意味着可以使用以下方法访问 Subject

Subject subject = Subject.getSubject(AccessController.getContext());

你可以使用 jaas-api-provision 属性来配置此集成。当与依赖于填充 JAAS 主体的旧版或外部 API 集成时,此功能非常有用。