JAAS是對(duì)JCE安全框架的重要補(bǔ)充,,通過提供認(rèn)證用戶和確定用戶授權(quán)來增強(qiáng)JAVA解決方案的動(dòng)態(tài)安全性,使得資源能夠得到很好得到保護(hù)和控制(JAAS使用動(dòng)態(tài)的安全策略來定義權(quán)限,,而不是將其靜態(tài)的嵌入到代碼中),。
JAAS
采用的是插件的運(yùn)行方式,一開始就被設(shè)計(jì)成可插拔的(Pluggable),,根據(jù)應(yīng)用的需要,,只要配置一下JAAS的配置文件,這些組件即可包含
在我們的應(yīng)用程序中,。使用JAAS包接口,,開發(fā)者和第三方可以開發(fā)一些組件或者BEAN來實(shí)現(xiàn)登陸認(rèn)證,,或者通過與使用者或外部的系統(tǒng)的進(jìn)行交互來訪問認(rèn)
證信息(當(dāng)然我們可以設(shè)計(jì)更為穩(wěn)妥安全的密碼學(xué)協(xié)議),。JAAS提供了一組用于用戶鑒別的類和接口,,這意味著支持JAAS的應(yīng)用會(huì)要求用戶登陸,同時(shí)
JAAS提供了另一組用于用戶授權(quán)的類和接口,。在討論例子之前,,先對(duì)JAAS API中常用的一些類和接口做個(gè)簡(jiǎn)單的說明。
LoginModule :確認(rèn)用戶的合法性(使用CallbackHandler或者其他類方法),,并分配訪問權(quán)限principal給subject,;
LoginContext:為了實(shí)現(xiàn)用戶鑒別,建立相應(yīng)的環(huán)境,,從配置文件中導(dǎo)入規(guī)則,;
CallbackHandler:回調(diào)處理器,負(fù)責(zé)與用戶(代碼擁有者和執(zhí)行者)交互,,確認(rèn)其身份的合法性,;
Subject:表示登陸處理的目標(biāo),即一個(gè)被鑒別的用戶,。并可關(guān)聯(lián)一個(gè)或多個(gè)pirncipal,;
Principal:表示具有訪問權(quán)限的一個(gè)實(shí)體,,可以看作是可以執(zhí)行某種操作的證件。
理
解這些類和接口的關(guān)系我給個(gè)生動(dòng)的比方:一個(gè)軍事學(xué)校,,入學(xué)的時(shí)候校方(LoginModule)根據(jù)學(xué)生(Subject)的入學(xué)通知來確定其合法
性,,這個(gè)過程交由某工作人員(CallbackHandler)執(zhí)行,(CallbackHandler)確認(rèn)后,,(LoginModule)給不同
(Subject)根據(jù)其身份發(fā)給相關(guān)的證件(Principal),,有了該證件就可以訪問對(duì)應(yīng)的資源,,(Subject)根據(jù)自己的
(Principal)的級(jí)別可以使用和訪問學(xué)校不同資源,。
一個(gè)(Subject)的(Principal)如果是士官級(jí),那么可以訪問的資源就相對(duì)少些,,如果是將軍級(jí)那就多些,。當(dāng)然一個(gè)(Subject)可以擁有多個(gè)(Principal)。
通過分析我們會(huì)發(fā)現(xiàn),,JAAS采用的也是身份檢查+權(quán)限分配模式,。因此JAAS的應(yīng)用也分成兩個(gè)部分:(1)認(rèn)證;(2)授權(quán),。過程是先認(rèn)證后根據(jù)身份來授權(quán)(有歧視的嫌疑的東東,,本人可是反歧視人士)。
那么JAAS是如何實(shí)現(xiàn)認(rèn)證的呢,?又是如何實(shí)現(xiàn)授權(quán)的呢,?且聽我慢慢分解,將其妙處展現(xiàn)給大家,。
二 JAAS的認(rèn)證原理
(1) 設(shè)置JAAS配置文件,,關(guān)于配置非常有技巧,跟設(shè)置防火墻的過濾規(guī)則有得一拼,;
(2) 根據(jù)JAAS配置文件的條目加載一個(gè)或者多個(gè)LoginModule(通常一個(gè),,也可以變態(tài)得使用多個(gè));
(3) 為了管理用戶認(rèn)證的有關(guān)過程,,將提供一個(gè)可選的LoginModule構(gòu)造函數(shù)和一個(gè)回調(diào)處理器CallbackHandler,。如果沒有在構(gòu)造函數(shù)中提供回調(diào)處理器,系統(tǒng)采用默認(rèn)設(shè)置,;
(4)
初始并實(shí)例化LoginContext(加載配置規(guī)則),,如果成功,則調(diào)用LoginContext的login方法,。無論是否需
要,,LoginContext都會(huì)去首先讀取JAAS配置文件,從中獲得要加載的登陸模塊信息,,其initialize方法將按照配置文件中的相關(guān)內(nèi)容提
供LoginModule運(yùn)行所需要的信息,;
(5) LoginContext的login方法將調(diào)用LoginModule的login方法,,確定用戶身份。該方法將設(shè)置相關(guān)的回調(diào),,并由回調(diào)處理器CallbackHandler來管理登陸處理回調(diào),;
(6)
LoginModule的login方法將負(fù)責(zé)與用戶進(jìn)行交互(可能是人機(jī)交互,也可能是機(jī)機(jī)交互),,如果用戶輸入信息無效,,則該方
法返回FALSE,一次交互過程結(jié)束,,如果用戶輸入信息有效,,則該方法將設(shè)置Principal對(duì)象的Subject對(duì)象,并返回TRUE,;當(dāng)然
LoginModule也可以將與用戶之間的所有交互過程全部委托給處理器CallbackHandler來處理,。如果登陸成功,
LoginContext將調(diào)用LoginModule的commit方法將結(jié)果提交給LoginModule實(shí)例的內(nèi)部狀態(tài),。
在應(yīng)用程序中使用JAAS驗(yàn)證通常會(huì)涉及到以下幾個(gè)步驟:
1. 創(chuàng)建一個(gè)LoginContext的實(shí)例,。
2. 為了能夠獲得和處理驗(yàn)證信息,將一個(gè)CallBackHandler對(duì)象作為參數(shù)傳送給LoginContext,。
3. 通過調(diào)用LoginContext的login()方法來進(jìn)行驗(yàn)證,。
4. 通過使用login()方法返回的Subject對(duì)象實(shí)現(xiàn)一些特殊的功能(假設(shè)登錄成功)。
下面是一個(gè)簡(jiǎn)單的例子:
SimpleLogin.java - package com;
-
- import javax.security.auth.login.LoginContext;
- import javax.security.auth.login.LoginException;
-
- public class SimpleLogin {
-
- public static void main(String[] args) {
-
-
- LoginContext loginContext = null;
- try {
-
- loginContext = new LoginContext("simple", new SimpleCallbackHandle());
- } catch (LoginException e) {
-
- System.out.println(e.getMessage());
- }
-
- try {
-
-
- loginContext.login();
- } catch (LoginException e) {
-
- }
- }
-
- }
SimpleCallbackHandle.java
- package com;
-
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import javax.security.auth.callback.Callback;
- import javax.security.auth.callback.CallbackHandler;
- import javax.security.auth.callback.NameCallback;
- import javax.security.auth.callback.PasswordCallback;
- import javax.security.auth.callback.UnsupportedCallbackException;
-
- public class SimpleCallbackHandle implements CallbackHandler {
-
- public void handle(Callback[] callbacks) throws IOException,
- UnsupportedCallbackException {
-
- for (Callback callback : callbacks) {
-
- if (callback instanceof NameCallback) {
- NameCallback nc = (NameCallback) callback;
-
- System.out.print(nc.getPrompt());
- System.out.flush();
-
- nc.setName((new BufferedReader(new InputStreamReader(
- System.in))).readLine());
- } else if (callback instanceof PasswordCallback) {
- PasswordCallback pcb = (PasswordCallback) callback;
-
- System.out.print(pcb.getPrompt());
- System.out.flush();
- pcb.setPassword((new BufferedReader(new InputStreamReader(
- System.in))).readLine().toCharArray());
- }
- }
- }
- }
SimpleLoginModule.java - package com;
-
- import java.io.IOException;
- import java.util.Map;
-
- import javax.security.auth.Subject;
- import javax.security.auth.callback.Callback;
- import javax.security.auth.callback.CallbackHandler;
- import javax.security.auth.callback.NameCallback;
- import javax.security.auth.callback.PasswordCallback;
- import javax.security.auth.callback.UnsupportedCallbackException;
- import javax.security.auth.login.LoginException;
- import javax.security.auth.spi.LoginModule;
-
- public class SimpleLoginModule implements LoginModule{
-
- private String userName;
-
- private char[] password;
-
- private Subject subject;
-
- private CallbackHandler callbackHandler;
-
- private Map sharedState;
-
- private Map options;
-
- private String debug;
-
- public boolean abort() throws LoginException {
- System.out.println("abort()");
- return false;
- }
-
- public boolean commit() throws LoginException {
- System.out.println("commit()");
- return false;
- }
-
- public void initialize(Subject subject, CallbackHandler callbackHandler,
- Map sharedState, Map options) {
-
- this.subject = subject;
- this.callbackHandler = callbackHandler;
- this.sharedState = sharedState;
- this.options = options;
-
- debug = (String)options.get("debug");
- }
-
- public boolean login() throws LoginException {
-
- Callback[] callbacks = new Callback[2];
- callbacks[0] = new NameCallback("用戶名: ");
- callbacks[1] = new PasswordCallback("密碼: ", false);
-
- try {
-
- callbackHandler.handle(callbacks);
- userName = ((NameCallback)callbacks[0]).getName();
- password = ((PasswordCallback)callbacks[1]).getPassword();
-
- if(debug.equals("true")){
- System.out.println("你輸入的用戶名為:" + userName);
- System.out.println("你輸入的密碼為:" + new String(password));
- }
-
- if(userName.equals("callan") && new String(password).equals("callanpass")){
- System.out.println("驗(yàn)證成功");
- return true;
- } else {
- System.out.println("驗(yàn)證失敗");
- userName = null;
- password = null;
- }
- } catch (IOException e) {
- e.printStackTrace();
- } catch (UnsupportedCallbackException e) {
- e.printStackTrace();
- }
-
- return false;
- }
-
- public boolean logout() throws LoginException {
- System.out.println("logout()");
- return false;
- }
-
- }
jaas.config - simple {
- com.SimpleLoginModule required debug=true;
- };
將代碼編輯通過后執(zhí)行以下命令: java -Djava.security.auth.login.config==jaas.config com.SimpleLogin
|