集成JAAS認(rèn)證
1. 前言
在前面的小節(jié)中,我們相繼介紹了集中應(yīng)用廣泛的統(tǒng)一身份認(rèn)證規(guī)范:OAuth2.0,SAML2.0 和 CAS。本節(jié)我們介紹一種 Java 早期的安全框架,已經(jīng)如何用 Spring Security 集成 JAAS。
JAAS 是「Java Authentication and Authorization Service」 的縮寫,其中文含義為「基于 Java 的認(rèn)證和授權(quán)服務(wù)」。JAAS 提供了靈活的彈性的機制保護了 Java 客戶端程序和 Java 服務(wù)端程序的安全。
Spring Security 提供了 JAAS 的集成功能。
2. JAAS 基本原理
JAAS 是 Java 早期的安全框架,重點用于驗證代碼的來源或者開發(fā)者,避免代碼被偽造或遭到篡改。JAAS 主要用在 C / S 應(yīng)用中,其驗證的對象是啟動程序的用戶。
JAAS 的特點是實現(xiàn)了「可插入認(rèn)證」。應(yīng)用程序與底層認(rèn)證相互獨立,也就是說在調(diào)整底層認(rèn)證方法的時候,不需要修改應(yīng)用程序本身。應(yīng)用程序通過配置文件,決定使用何種認(rèn)證方法。
3. Spring Security 實現(xiàn)方法
Spring Security 對 JAAS 的支持主要包含如下幾個對象:
- AbstractJaasAuthenticationProvider
- DefaultJaasAuthenticationProvider
- JaasAuthenticationProvider
我們對幾個對象逐一進行解讀。
3.1 AbstractJaasAuthenticationProvider
Spring Security 對 JAAS 認(rèn)證的核心實現(xiàn)是 AbstractJaasAuthenticationProvider
類。它的核心行為是創(chuàng)建 LoginContext
對象。它還包括幾個重要的依賴注入項:JAAS CallbackHandler
和 JAAS AuthorityGranter
。
3.1.1 JAAS CallbackHandler
大多數(shù)的 JAAS 認(rèn)證模塊包含一系列有序的回調(diào)方法,這些回調(diào)方法通常用來維護用戶的認(rèn)證信息。
在 Spring Security 項目開發(fā)中,當(dāng)使用 JAAS 方式完成認(rèn)證,Spring Security 的認(rèn)證機制已經(jīng)為 JAAS 登錄準(zhǔn)備了足夠的認(rèn)證信息,并將其保存為 Authentication
對象中。同時,Spring Security 為 JAAS 認(rèn)證提供了兩個默認(rèn)的回調(diào)接口:JaasNameCallbackHandler
和 JaasPasswordCallbackHandler
。這兩個回調(diào)接口同時實現(xiàn)自 JaasAuthenticationCallbackHandler
接口。一般情況下,我們可以直接使用這些接口,而不用考慮實現(xiàn)細(xì)節(jié)。對于那些需要完全掌握回調(diào)行的開發(fā)者來說,抽象類 AbstractJaasAuthenticationProvider
為這些回調(diào)接口提供了一個基礎(chǔ)實現(xiàn),即一個真正實現(xiàn)了 JAAS 回調(diào)接口的實現(xiàn)類,名為 InternalCallbackHandler
。如果認(rèn)證模塊向 InternalCallbackHandler
請求回調(diào),回調(diào)會按順序調(diào)用 JaasAuthenticationCallbackHandler
。
3.1.2 JAAS AuthorityGranter
JAAS 使用 Principal 身份主體作為鑒權(quán)依據(jù),其角色信息包含在身份主體對象中。相對應(yīng)的,Spring Security 的權(quán)限信息包含在 Authentication
對象中。每個 Authentication
對象都包含了單一的身份主體信息,和多個權(quán)限信息。為了匹配兩個不相同的權(quán)限模型,Spring Security 在 JAAS 認(rèn)證模塊組件中,提供了 AuthorityGranter
接口。
AuthorityGranter
的作用是核實 JAAS 的身份主體,并返回一系列的表示權(quán)限信息的字符串。對每個返回的權(quán)限信息字符串,AbstractJaasAuthenticationProvider
都會創(chuàng)建一個 JaasGrantedAuthority
對象,時期包含權(quán)限信息和身份主體信息。這些信息在 JAAS LoginModule
首次認(rèn)證通過后,由 AbstractJaasAuthenticationProvider
負(fù)責(zé)生成,并將其賦予到 LoginContext
實例中。我們可以通過LoginContext.getSubject().getPrincipals()
方式獲取身份信息和權(quán)限信息。
Spring Security 并未包含 AuthorityGranter
的具體實現(xiàn),但是我們可以參考其單元測試中 TestAuthorityGranter
的方式進行擴展或自定義。
3.2 DefaultJaasAuthenticationProvider
DefaultJaasAuthenticationProvider
對象允許注入 JAAS 的相關(guān)配置,然后會用該配置創(chuàng)建 LoginContext
上下文,這意味著 DefaultJaasAuthenticationProvider
對象并未綁定任何的具體配置內(nèi)容。
JAAS 的配置對象 Configuration
有一個簡單實現(xiàn) InMemoryConfiguration
,利用內(nèi)存保存和獲取配置信息。在該類的構(gòu)造方法里,我們可以將配置項以 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 是認(rèn)證上下文的默認(rèn)名稱
-->
<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 實現(xiàn) -->
<bean class="org.springframework.security.authentication.jaas.TestAuthorityGranter"/>
</list>
</property>
</bean>
3.3 JaasAuthenticationProvider
JaasAuthenticationProvider
的使用默認(rèn)的配置對象創(chuàng)建上線文對象 LoginContext
。
假設(shè)我們有一個 JAAS 認(rèn)證的配置文件 /WEB-INF/login.conf
,其內(nèi)容如下
JAASTest {
sample.SampleLoginModule required;
};
和眾多的 Spring Security bean 對象相同,JaasAuthenticationProvider
對象也通過 Spring 應(yīng)用上下文配置,以下為通過 Spring 配置文件配置 JAAS 認(rèn)證。
<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
屬性快速集成,當(dāng)我們需要擴展 JAAS 主題時會被使用到。
4. 小結(jié)
本節(jié)的主要知識點如下:
- JAAS 是 Java 原生支持的一種底層認(rèn)證方式;
- JAAS 認(rèn)證的是應(yīng)用程序的啟動用戶;
- JAAS 認(rèn)證與應(yīng)用程序相互獨立;
- Spring Security 為 JAAS 認(rèn)證提供了代理接口和統(tǒng)一的配置方式。
JAAS 在現(xiàn)在的應(yīng)用環(huán)境中已經(jīng)非常少見了,不過作為 Java 早期的安全框架,它對我們理解 Java 應(yīng)用認(rèn)證和鑒權(quán)的邏輯很有幫助。下節(jié)我們介紹一個在互聯(lián)網(wǎng)環(huán)境下共享身份的解決方案:「OpenID」以及 Spring Security 與其集成的方法。