集成JAAS认证
1. 前言
在前面的小节中,我们相继介绍了集中应用广泛的统一身份认证规范:OAuth2.0,SAML2.0 和 CAS。本节我们介绍一种 Java 早期的安全框架,已经如何用 Spring Security 集成 JAAS。
JAAS 是「Java Authentication and Authorization Service」 的缩写,其中文含义为「基于 Java 的认证和授权服务」。JAAS 提供了灵活的弹性的机制保护了 Java 客户端程序和 Java 服务端程序的安全。
Spring Security 提供了 JAAS 的集成功能。
2. JAAS 基本原理
JAAS 是 Java 早期的安全框架,重点用于验证代码的来源或者开发者,避免代码被伪造或遭到篡改。JAAS 主要用在 C / S 应用中,其验证的对象是启动程序的用户。
JAAS 的特点是实现了「可插入认证」。应用程序与底层认证相互独立,也就是说在调整底层认证方法的时候,不需要修改应用程序本身。应用程序通过配置文件,决定使用何种认证方法。
3. Spring Security 实现方法
Spring Security 对 JAAS 的支持主要包含如下几个对象:
- AbstractJaasAuthenticationProvider
- DefaultJaasAuthenticationProvider
- JaasAuthenticationProvider
我们对几个对象逐一进行解读。
3.1 AbstractJaasAuthenticationProvider
Spring Security 对 JAAS 认证的核心实现是 AbstractJaasAuthenticationProvider
类。它的核心行为是创建 LoginContext
对象。它还包括几个重要的依赖注入项:JAAS CallbackHandler
和 JAAS AuthorityGranter
。
3.1.1 JAAS CallbackHandler
大多数的 JAAS 认证模块包含一系列有序的回调方法,这些回调方法通常用来维护用户的认证信息。
在 Spring Security 项目开发中,当使用 JAAS 方式完成认证,Spring Security 的认证机制已经为 JAAS 登录准备了足够的认证信息,并将其保存为 Authentication
对象中。同时,Spring Security 为 JAAS 认证提供了两个默认的回调接口:JaasNameCallbackHandler
和 JaasPasswordCallbackHandler
。这两个回调接口同时实现自 JaasAuthenticationCallbackHandler
接口。一般情况下,我们可以直接使用这些接口,而不用考虑实现细节。对于那些需要完全掌握回调行的开发者来说,抽象类 AbstractJaasAuthenticationProvider
为这些回调接口提供了一个基础实现,即一个真正实现了 JAAS 回调接口的实现类,名为 InternalCallbackHandler
。如果认证模块向 InternalCallbackHandler
请求回调,回调会按顺序调用 JaasAuthenticationCallbackHandler
。
3.1.2 JAAS AuthorityGranter
JAAS 使用 Principal 身份主体作为鉴权依据,其角色信息包含在身份主体对象中。相对应的,Spring Security 的权限信息包含在 Authentication
对象中。每个 Authentication
对象都包含了单一的身份主体信息,和多个权限信息。为了匹配两个不相同的权限模型,Spring Security 在 JAAS 认证模块组件中,提供了 AuthorityGranter
接口。
AuthorityGranter
的作用是核实 JAAS 的身份主体,并返回一系列的表示权限信息的字符串。对每个返回的权限信息字符串,AbstractJaasAuthenticationProvider
都会创建一个 JaasGrantedAuthority
对象,时期包含权限信息和身份主体信息。这些信息在 JAAS LoginModule
首次认证通过后,由 AbstractJaasAuthenticationProvider
负责生成,并将其赋予到 LoginContext
实例中。我们可以通过LoginContext.getSubject().getPrincipals()
方式获取身份信息和权限信息。
Spring Security 并未包含 AuthorityGranter
的具体实现,但是我们可以参考其单元测试中 TestAuthorityGranter
的方式进行扩展或自定义。
3.2 DefaultJaasAuthenticationProvider
DefaultJaasAuthenticationProvider
对象允许注入 JAAS 的相关配置,然后会用该配置创建 LoginContext
上下文,这意味着 DefaultJaasAuthenticationProvider
对象并未绑定任何的具体配置内容。
JAAS 的配置对象 Configuration
有一个简单实现 InMemoryConfiguration
,利用内存保存和获取配置信息。在该类的构造方法里,我们可以将配置项以 Map
的形式逐一配置进来。
下面我们展示一个配置实例:使用 DefaultJaasAuthenticationProvider
和 InMemoryConfiguration
。
<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 是认证上下文的默认名称
-->
<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>
<!-- 这里可以配置我们自定义的 AuthorityGranter 实现 -->
<bean class="org.springframework.security.authentication.jaas.TestAuthorityGranter"/>
</list>
</property>
</bean>
3.3 JaasAuthenticationProvider
JaasAuthenticationProvider
的使用默认的配置对象创建上线文对象 LoginContext
。
假设我们有一个 JAAS 认证的配置文件 /WEB-INF/login.conf
,其内容如下
JAASTest {
sample.SampleLoginModule required;
};
和众多的 Spring Security bean 对象相同,JaasAuthenticationProvider
对象也通过 Spring 应用上下文配置,以下为通过 Spring 配置文件配置 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>
10.16.5. 启动 Subject
如果配置完整,JaasApiIntegrationFilter
对象将在 JaasAuthenticationToken
对象中启动主题对象 Subject
,并可通过以下形式获得:
Subject subject = Subject.getSubject(AccessController.getContext());
这种集成方式可以通过 jaas-api-provision
属性快速集成,当我们需要扩展 JAAS 主题时会被使用到。
4. 小结
本节的主要知识点如下:
- JAAS 是 Java 原生支持的一种底层认证方式;
- JAAS 认证的是应用程序的启动用户;
- JAAS 认证与应用程序相互独立;
- Spring Security 为 JAAS 认证提供了代理接口和统一的配置方式。
JAAS 在现在的应用环境中已经非常少见了,不过作为 Java 早期的安全框架,它对我们理解 Java 应用认证和鉴权的逻辑很有帮助。下节我们介绍一个在互联网环境下共享身份的解决方案:「OpenID」以及 Spring Security 与其集成的方法。