集成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 CallbackHandlerJAAS AuthorityGranter

3.1.1 JAAS CallbackHandler

大多数的 JAAS 认证模块包含一系列有序的回调方法,这些回调方法通常用来维护用户的认证信息。

在 Spring Security 项目开发中,当使用 JAAS 方式完成认证,Spring Security 的认证机制已经为 JAAS 登录准备了足够的认证信息,并将其保存为 Authentication 对象中。同时,Spring Security 为 JAAS 认证提供了两个默认的回调接口:JaasNameCallbackHandlerJaasPasswordCallbackHandler。这两个回调接口同时实现自 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 的形式逐一配置进来。

下面我们展示一个配置实例:使用 DefaultJaasAuthenticationProviderInMemoryConfiguration

<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 与其集成的方法。