Java平臺(tái)是創(chuàng)建企業(yè)應(yīng)用程序的普遍選擇。它所以受歡迎,主要原因之一是在創(chuàng)建Java語(yǔ)言時(shí)充分考慮了安全性,而且市場(chǎng)上也普遍認(rèn)為Java是一種"安全的"語(yǔ)言。Java平臺(tái)在兩個(gè)層次上提供安全性:語(yǔ)言層次安全性和企業(yè)層次安全性。
1.語(yǔ)言層次安全性
最初的Java(JDK1.2)平臺(tái)采用沙箱安全模型,基本安全模型由三部分來(lái)承擔(dān),這三部分構(gòu)成Java運(yùn)行環(huán)境的三個(gè)安全組件,分別是:類加載器,文件校驗(yàn)器,安全管理器
1.1類加載器
類加載器負(fù)責(zé)從特定位置加載類,類加載器是JVM的看門人,控制著哪些代碼被加載或被拒絕,類加載器首先進(jìn)行安全性檢查,它往往從以下幾個(gè)方面去檢查,裝入的字節(jié)碼是否生成指針,裝入的字節(jié)碼是否違反訪問(wèn)限制,裝入的字節(jié)碼是否把對(duì)象當(dāng)作它們本身來(lái)訪問(wèn),它在確保執(zhí)行代碼的安全性方面至關(guān)重要。
1.2類文件校驗(yàn)器的校驗(yàn)
類文件校驗(yàn)器負(fù)責(zé)檢查那些無(wú)法執(zhí)行的明顯有破壞性的操作,類文件校驗(yàn)器執(zhí)行的一些檢查通常有:變量要在使用之前進(jìn)行初始化;方法調(diào)用和對(duì)象引用類型之間要匹配;沒(méi)有違反訪問(wèn)私有數(shù)據(jù)和方法的規(guī)則;對(duì)本地變量的訪問(wèn)都在運(yùn)行時(shí)堆棧內(nèi);運(yùn)行時(shí)堆棧是否溢出.如果以上檢查中任何一條沒(méi)有通過(guò),就認(rèn)為該類遭到了破壞,不被加載。
1.3安全管理器
一旦某個(gè)類被類加載器加載到虛擬機(jī)中,并由類文件校驗(yàn)器檢查過(guò)之后,JAVA的第三種安全機(jī)制安全管理器就會(huì)啟動(dòng),安全管理器是一個(gè)負(fù)責(zé)控制某個(gè)操作是否允許執(zhí)行的類,安全管理器負(fù)責(zé)檢查包括以下幾個(gè)方面的操作:當(dāng)前線程是否能創(chuàng)建一個(gè)新的類加載器;當(dāng)前線程是否能終止JVM的運(yùn)行;某個(gè)類是否能訪問(wèn)另一個(gè)類的成員;當(dāng)前線程是否能訪問(wèn)本地文件;當(dāng)前線程是否能打開(kāi)到達(dá)外部記住的socket連接;某個(gè)類是否能啟動(dòng)打印作業(yè);某個(gè)類是否能訪問(wèn)系統(tǒng)剪貼板;某個(gè)類是否能訪問(wèn)AWT事件隊(duì)列;當(dāng)前線程是否可被信任以打開(kāi)一頂層窗口。
盡管Java安全的支柱類加載器、類文件校驗(yàn)器、安全管理器每一個(gè)都有獨(dú)特的功能,但它們又相互依賴、相輔相承。共同保證了Java語(yǔ)言的安全性。
2.企業(yè)層次的安全特性
即是構(gòu)建安全的J2EE應(yīng)用。Java平臺(tái)在提供語(yǔ)言安全性的同時(shí)還提供其他API功能,為企業(yè)應(yīng)用程序提供一個(gè)總體的安全性解決方案。下面將介紹幾種方案。
2.1 Java加密擴(kuò)展(JCE)
JCE是一組包,為加密、密鑰生成、密鑰協(xié)商和消息身份驗(yàn)證代碼(MAC)算法提供一種框架和實(shí)現(xiàn)。JCE支持多種類型的加密,包括對(duì)稱的、非對(duì)稱的、塊和流密碼。在JDK 1.4之前,JCE是一個(gè)可選的包,現(xiàn)在它已經(jīng)成為Java平臺(tái)的一個(gè)標(biāo)準(zhǔn)組成部分。
2.2 Java安全套接字?jǐn)U展(JSSE)
JSSE是支持安全的Internet通信的一組包。它是實(shí)現(xiàn)了SSL和傳輸層安全(TLS)協(xié)議的JAVA技術(shù)。它包括用于數(shù)據(jù)加密、服務(wù)器身份驗(yàn)證、消息完整性和可選的客戶端身份驗(yàn)證的諸多功能。JSSE已被集成到JDK 1.4以上版本的平臺(tái)中。
2.3 Java身份驗(yàn)證和授權(quán)規(guī)范(JAAS)
JAAS通過(guò)對(duì)運(yùn)行程序的用戶的進(jìn)行驗(yàn)證,從而達(dá)到保護(hù)系統(tǒng)的目的。JAAS主要由兩個(gè)部件構(gòu)成,認(rèn)證和授權(quán),JAAS通過(guò)一個(gè)配置文件來(lái)定義認(rèn)證機(jī)制。認(rèn)證模塊是基于可插的認(rèn)證模塊而設(shè)計(jì)的,它可以運(yùn)行在客戶端和服務(wù)器端,授權(quán)模塊的設(shè)計(jì)是一個(gè)變化的過(guò)程,為了對(duì)資源的訪問(wèn)請(qǐng)求進(jìn)行授權(quán),首先需要應(yīng)用程序認(rèn)證請(qǐng)求的資源,subject術(shù)語(yǔ)來(lái)表示請(qǐng)求的資源,用java.security.auth.Subject類來(lái)表示subject。subject一旦通過(guò)了認(rèn)證,就會(huì)和身份和主體想關(guān)聯(lián)。在JAAS中將主體表示為javax.security.Principal對(duì)象,一個(gè)subject可能包含多個(gè)主題,除了和主題相關(guān)聯(lián)外,subject還可能擁有與安全相關(guān)的屬性或證書,證書是用戶的數(shù)據(jù),它包含這樣的認(rèn)證信息即認(rèn)證subject所擁有的其他服務(wù)的信息。基于J2EE的分布式應(yīng)用程序使用
JAAS一般有兩種情況:第一種情況,一個(gè)單獨(dú)的應(yīng)用系統(tǒng)與一個(gè)遠(yuǎn)程的EJB系統(tǒng)連接,用戶必須向應(yīng)用系統(tǒng)提供證明身份的信息或應(yīng)用系統(tǒng)向文件和其它的系統(tǒng)來(lái)檢索可證明身份的信息。這個(gè)單獨(dú)的應(yīng)用系統(tǒng)將在調(diào)用EJB組件之前使用JAAS來(lái)驗(yàn)證用戶,由應(yīng)用服務(wù)器完成驗(yàn)證的任務(wù)。只有當(dāng)用戶通過(guò)JAAS的驗(yàn)證之后,客戶端程序才可被信任地調(diào)用EJB方法。第二種情況,基于Web瀏覽器的客戶端程序連接到Servlet/JSP層,客戶端用戶將向Servlet/JSP層提供證明身份的信息,而Servlet/JSP層可以采用JAAS驗(yàn)證用戶。Web客戶端一般可以采
用基本驗(yàn)證、基于表格的驗(yàn)證、摘要驗(yàn)證、證書驗(yàn)證等方式來(lái)提供證明身份的信息。這種支持選擇不同認(rèn)證方法的靈活性有助于支持在管理員層實(shí)施更為復(fù)雜的安全策略,而不是在編程層上去實(shí)現(xiàn)。一旦客戶端通過(guò)應(yīng)用服務(wù)器認(rèn)證,安全上下文環(huán)境能被傳播到EJB層。在應(yīng)用程序中使用JAAS驗(yàn)證通常會(huì)涉及到以下幾個(gè)步驟:
1.創(chuàng)建一個(gè)LoginContext的實(shí)例。并傳遞LoginModule配置
文件程序段和CallbackHandler的名稱。
2.為了能夠獲得和處理驗(yàn)證信息,將一個(gè)CallbackHandler
對(duì)象作為參數(shù)傳送給LoginContext。
3.通過(guò)調(diào)用LoginContext的login()方法來(lái)進(jìn)行驗(yàn)證。
4.通過(guò)使用login()方法返回的Subject對(duì)象實(shí)現(xiàn)一些特殊
的功能(假設(shè)登錄成功)。
舉個(gè)例子:
LoginModel是jaas的一個(gè)核心接口,她負(fù)責(zé)實(shí)施用戶認(rèn)證。同時(shí)暴漏了initialize(),login(),commit(),abort(),logout()方法。
package sample;
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;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* @author worldheart
*
*/
public class ScreenContentLoginModule implements LoginModule {
protected static final Log log = LogFactory.getLog(ScreenContentLoginModule.class);
private Subject subject;
private UsernamePasswordPrincipal principal;
private CallbackHandler callbackhandler;
public ScreenContentLoginModule() {
log.info("ScreenContentLoginModule()................");
}
//在初始化LoginModel后,LoginContext會(huì)調(diào)用這一方法,從而完成當(dāng)前LoginModel的初始化工作
public void initialize(Subject subject, CallbackHandler
callbackhandler, Map state, Map options) {
log.info("進(jìn)入initialize()................");
this.principal = null;
this.subject = subject;
this.callbackhandler = callbackhandler;
}
//用戶階段1,并認(rèn)證subject的方法,它可能會(huì)收集用戶的憑證,比如用戶名,密碼,并將認(rèn)證結(jié)果存儲(chǔ)到LoginModel的實(shí)例中
public boolean login() throws LoginException {
log.info("進(jìn)入login()................");
Callback callbacks[] = new Callback[2];
callbacks[0] = new NameCallback("您的登錄名:");
callbacks[1] = new PasswordCallback("您的密碼:", false);
String username = null;
String password = null;
try {
this.callbackhandler.handle(callbacks);
username = ((NameCallback) callbacks[0]).getName();
password = new String(((PasswordCallback) callbacks[1]).getPassword());
} catch (java.io.IOException ioe) {
throw new LoginException(ioe.toString());
} catch (UnsupportedCallbackException ce) {
throw new LoginException(ce.getCallback().toString());
}
if (username.equals("marissa") || username.equals("scott")) {
this.principal = new UsernamePasswordPrincipal(username,password);
return true;
} else {
return false;
}
}
//表明認(rèn)證操作成功,會(huì)獲得階段1(login())的認(rèn)證結(jié)果,并將這一結(jié)果填充到subject中
public boolean commit() throws LoginException {
log.info("進(jìn)入commit()................");
if(this.principal == null)
return false;
this.subject.getPrincipals().add(this.principal);
return true;
}
//認(rèn)證操作失敗
public boolean abort() throws LoginException {
log.info("進(jìn)入abort()................");
if(this.principal == null)
return false;
this.principal = null;
return true;
}
//完成subject的銷毀工作
public boolean logout() throws LoginException {
log.info("進(jìn)入logout()................");
if(this.principal == null)
return false;
this.subject.getPrincipals().remove(principal);
this.principal = null;
return true;
}
}
下面是UsernamePasswordCallbackhandler類:
package sample;
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;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* @author worldheart
*
*/
public class UsernamePasswordCallbackHandler implements CallbackHandler {
protected static final Log log = LogFactory.getLog(UsernamePasswordCallbackHandler.class);
public void handle(Callback callbacks[]) throws IOException, UnsupportedCallbackException {
log.info("進(jìn)入handle()........................");
for (Callback cb: callbacks) {
if (cb instanceof NameCallback) {
NameCallback nc = (NameCallback) cb;
log.info(nc.getPrompt());
//采集用戶名
String username = (new BufferedReader(new InputStreamReader(
System.in))).readLine();
nc.setName(username);
} else if(cb instanceof PasswordCallback){
PasswordCallback pc = (PasswordCallback) cb;
log.info(pc.getPrompt());
//采集用戶密碼
String password = (new BufferedReader(new InputStreamReader(
System.in))).readLine();
pc.setPassword(password.toCharArray());
}
}
}
}
一 旦用戶收集到用戶賬號(hào)后NameCallback,PasswordCallback對(duì)象都會(huì)存儲(chǔ)他們,與此同時(shí),上述login()方法會(huì)基于賬號(hào)信構(gòu) 建UsernamePasswordPrincipal對(duì)象,并保留在登錄模塊中,而且login()會(huì)返回true,當(dāng)login方法順利完成用戶憑證 信息的收集工作后,commit會(huì)被觸發(fā),她將UsernamePasswordPrincipal對(duì)象擺到Subject對(duì)象中。
當(dāng)login方法未能順利完成用戶憑證信息的收集工作后,abort會(huì)被觸發(fā),將principal等信息破換掉。當(dāng)?shù)卿浻脩敉隄M的完成自身的業(yè)務(wù)操作后便可以考慮退出當(dāng)前的應(yīng)用,調(diào)用logout方法。下面是Principal對(duì)象:
package sample;
import java.security.Principal;
/**
*
* @author worldheart
*
*/
//Acegi中的Authentication接口繼承了Principal接口
public class UsernamePasswordPrincipal implements Principal {
private String username;
private String password;
//存儲(chǔ)用戶名、密碼,比如marissa/koala
public UsernamePasswordPrincipal(String username, String password) {
this.username = username;
this.password = password;
}
public String getName() {
return this.username;
}
public String toString() {
return this.username + "->" + this.password;
}
}
為了使用上述登錄模塊,需要準(zhǔn)備一個(gè)jaas配置文件:
Loginmodel.conf放在src下面
ScreenContent {
sample.ScreenContentLoginModule required;
};
客戶應(yīng)用:
package sample;
import java.io.File;
import java.security.PrivilegedAction;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* @author worldheart
*
*/
public class JaasSecurityClient {
protected static final Log log = LogFactory
.getLog(JaasSecurityClient.class);
public static void main(String argv[]) throws LoginException,
SecurityException {
LoginContext ctx = null;
ctx = new LoginContext("ScreenContent", new UsernamePasswordCallbackHandler());
//marissa用戶登錄到當(dāng)前應(yīng)用中
ctx.login();
log.info("當(dāng)前用戶已經(jīng)通過(guò)用戶認(rèn)證");
Subject subject = ctx.getSubject();
log.info(subject);
// log.info("啟用JAAS用戶授權(quán)能力");
// log.info("臨時(shí)目錄為," + Subject.doAsPrivileged(subject, new PrivilegedAction() {
// public Object run() {
// log.info("當(dāng)前用戶正在經(jīng)過(guò)JAAS授權(quán)操作的考驗(yàn),并正調(diào)用目標(biāo)業(yè)務(wù)操作");
// new File("D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf").exists();
// return System.getProperty("java.io.tmpdir");
// }
// }, null));
// 退出當(dāng)前已登錄marissa用戶
ctx.logout();
}
}
在運(yùn)行客戶應(yīng)用之前還需要提供JVM參數(shù),即引用到loginmoudel.conf配置文件:
-Djava.security.auth.login.config=src/loginmoudel.conf
或者通過(guò)javahome/jre/lib/security目錄中的java.security配置文件指定上述loginmoudel.conf配置文件:
#login.config.url.l=file:${user.home}/.java.login.config
login.config.url.l=file:d:/eclipse/src/loginmoudel.conf
SecurityContextLoginModule是 Acegi內(nèi)置的一個(gè)LoginModel實(shí)現(xiàn),當(dāng)開(kāi)發(fā)Jaas應(yīng)用時(shí),用戶憑證信息的獲取可能來(lái)自Acegi,此時(shí),我們便可以采用內(nèi)置的 SecurityContextLoginModel。要使用SecurityContextLoginModule,我們需要在Jaas配置文件中配置 它:
ACEGI {
org.acegisecurity.providers.jaas.SecurityContextLoginModule
required ignoreMissingAuthentication=true;
};
客戶端應(yīng)用:
package sample;
import java.io.File;
import java.security.PrivilegedAction;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* @author worldheart
*
*/
public class AcegiSecurityClient {
protected static final Log log = LogFactory
.getLog(AcegiSecurityClient.class);
public static void main(String argv[]) throws LoginException, SecurityException {
LoginContext ctx = null;
//在實(shí)際企業(yè)應(yīng)用中,Authentication對(duì)象的構(gòu)建形式多種多樣
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken("marissa", "koala"));
ctx = new LoginContext("ACEGI");
// marissa用戶登錄到當(dāng)前應(yīng)用中
ctx.login();
log.info("當(dāng)前用戶已經(jīng)通過(guò)用戶認(rèn)證");
Subject subject = ctx.getSubject();
log.info(subject);
// log.info("啟用JAAS用戶授權(quán)能力");
// log.info("臨時(shí)目錄為,"
// + Subject.doAsPrivileged(subject, new PrivilegedAction() {
// public Object run() {
// log.info("當(dāng)前用戶正在經(jīng)過(guò)JAAS授權(quán)操作的考驗(yàn),并正調(diào)用目標(biāo)業(yè)務(wù)操作");
// new File(
// "D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf")
// .exists();
// return System.getProperty("java.io.tmpdir");
// }
// }, null));
// 退出當(dāng)前已登錄marissa用戶
ctx.logout();
//清除已注冊(cè)的SecurityContext
SecurityContextHolder.clearContext();
}
}
注意到我們并未為L(zhǎng)oginContext提供CallbackHandler對(duì)象,由于Acegi負(fù)責(zé)提供兼容于Principal的Authentication對(duì)象,因此用戶憑證的收集也不用CallbackHandler操心了。
在運(yùn)行客戶應(yīng)用之前還需要提供JVM參數(shù),即引用到loginmoudel.conf配置文件:
-Djava.security.auth.login.config=src/loginmoudel.conf
或者通過(guò)javahome/jre/lib/security目錄中的java.security配置文件指定上述loginmoudel.conf配置文件:
#login.config.url.l=file:${user.home}/.java.login.config
login.config.url.l=file:d:/eclipse/src/loginmoudel.conf
啟 用Java安全管理器:大部分java開(kāi)發(fā)者都知道,借助如下JVM參數(shù)能夠啟用java安全管理器,-Djava.security.manager。 既然如此,我們通過(guò)如下JVM參數(shù)運(yùn)行JaasSecurityClient客戶端和AcegiSecurityClient客戶端:
-Djava.security.manager -Djava.security.auth.login.config=src/loginmodule.conf
但是這樣會(huì)出錯(cuò):java.security.auth.login.config.AccessControlException:access denied
出 錯(cuò)原因:默認(rèn)時(shí),直接借助“-Djava.security.manager”啟動(dòng)java安全管理器,JVM會(huì)采用javahome/jre/lib /security中的java.policy策略文件,而這一策略文件并未對(duì)上述涉及到的各種權(quán)限(比 如:createLoginContext.ScreenContent,讀取acegi.security.strategyJava屬性)進(jìn)行授權(quán)因 此拋出了異常。
為此我們可以提供新的授權(quán)信息jaassecuritypolicy.txt策略文件。由于我們需要同LoginContext進(jìn)行各類操作因此需要提供相關(guān)AuthPermission權(quán)限給Acegi
SecurityClient,同時(shí)我們使用了Commons-Logging,Log4j管理日志,因此還必須將相應(yīng)的操作權(quán)限給這一客戶,在操作日志的過(guò)程中,客戶應(yīng)用需要操控的:d:/ddlog.log日志文件因此需要將讀寫權(quán)限授給這一客戶應(yīng)用。
grant codebase "file:./-"{
permission java.io.FilePermission "D:/contactsforchapter8.log", "read, write";
permission javax.security.auth.AuthPermission "createLoginContext";
permission javax.security.auth.AuthPermission "modifyPrincipals";
};
grant codeBase
"file:D:/eclipse/workspace/contactsforchapter8/context/WEB-INF/lib/log4j-1.2.14.jar" {
permission java.security.AllPermission;
};
grant codeBase
"file:D:/eclipse/workspace/contactsforchapter8/context/WEB-INF/lib/commons-logging-1.0.4.jar" {
permission java.security.AllPermission;
};
實(shí)際上java的策略文件編寫可以通過(guò)policytool工具。
運(yùn)行JaasSecurityClient客戶端應(yīng)用:
-Djava.security.manager -Djava.security.policy=src/jaassecuriypolicy.txt
-Djava.security.auth.login.config=src/loginmodule.conf
類似的運(yùn)行AcegiSecurityClient的策略文件:
grant codebase "file:./-"{
permission java.util.PropertyPermission "acegi.security.strategy", "read";
permission java.io.FilePermission "D:/contactsforchapter8.log", "read, write";
permission javax.security.auth.AuthPermission "createLoginContext";
permission javax.security.auth.AuthPermission "modifyPrincipals";
};
grant codeBase
"file:D:/eclipse/workspace/contactsforchapter8/context/WEB-INF/lib/log4j-1.2.14.jar" {
permission java.security.AllPermission;
};
grant codeBase
"file:D:/eclipse/workspace/contactsforchapter8/context/WEB-INF/lib/commons-logging-1.0.4.jar" {
permission java.security.AllPermission;
};
運(yùn)行AcegiSecurityClient客戶端應(yīng)用:
-Djava.security.manager -Djava.security.policy=src/acegisecuriypolicy.txt
-Djava.security.auth.login.config=src/loginmodule.conf
啟用Jaas的用戶授權(quán)功能:jaas的授權(quán)能力依賴java策略文件,下面提供了另一個(gè)版本的jaasSecurityClient客戶應(yīng)用,新增了兩行java代碼:
LoginContext ctx = null;
ctx = new LoginContext("ScreenContent", new UsernamePasswordCallbackHandler());
//marissa用戶登錄到當(dāng)前應(yīng)用中
ctx.login();
log.info("當(dāng)前用戶已經(jīng)通過(guò)用戶認(rèn)證");
Subject subject = ctx.getSubject();
log.info(subject);
new File("D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf").exists();
System.getProperty("java.io.tmpdir");
// 退出當(dāng)前已登錄marissa用戶
ctx.logout();
此時(shí)開(kāi)發(fā)者必須往jaassecuritypolicy.txt策略文件中添加如下權(quán)限到其中:
permission java.io.FilePermission "D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf", "read";
permission java.util.PropertyPermission "java.io.tmpdir", "READ";
如果客戶要求只具有marissa用戶才有權(quán)利運(yùn)行上述兩行代碼,那么應(yīng)該這樣:
LoginContext ctx = null;
ctx = new LoginContext("ScreenContent", new UsernamePasswordCallbackHandler());
//marissa用戶登錄到當(dāng)前應(yīng)用中
ctx.login();
log.info("當(dāng)前用戶已經(jīng)通過(guò)用戶認(rèn)證");
Subject subject = ctx.getSubject();
log.info(subject);
// log.info("啟用JAAS用戶授權(quán)能力");
// log.info("臨時(shí)目錄為," + Subject.doAsPrivileged(subject, new PrivilegedAction() {
// public Object run() {
// log.info("當(dāng)前用戶正在經(jīng)過(guò)JAAS授權(quán)操作的考驗(yàn),并正調(diào)用目標(biāo)業(yè)務(wù)操作");
// new File("D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf").exists();
// return System.getProperty("java.io.tmpdir");
// }
// }, null));
// 退出當(dāng)前已登錄marissa用戶
ctx.logout();
那么jaassecuritypolicy.txt策略文件應(yīng)該添加如下內(nèi)容:
grant codebase "file:./-",
Principal sample.UsernamePasswordPrincipal "marissa" {
permission java.io.FilePermission "D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf", "read";
permission java.util.PropertyPermission "java.io.tmpdir", "READ";
};
啟動(dòng)jaassecurityclient客戶端:
-Djava.security.manager -Djava.security.policy=src/jaassecuriypolicy.txt
-Djava.security.auth.login.config=src/loginmodule.conf
那么對(duì)于acegisecurityclient客戶應(yīng)用,acegisecuritypolicy.txt應(yīng)該增加:
grant codebase "file:./-",
Principal org.acegisecurity.providers.UsernamePasswordAuthenticationToken "marissa" {
permission java.io.FilePermission "D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf", "read";
permission java.util.PropertyPermission "java.io.tmpdir", "read";
};
啟動(dòng):
-Djava.security.manager -Djava.security.policy=src/acegisecuriypolicy.txt
-Djava.security.auth.login.config=src/loginmodule.conf
直擊JaasAuthenticationProvider
配置:
<bean id="jaasAuthenticationProvider" class="org.acegisecurity.providers.jaas.JaasAuthenticationProvider">
<property name="authorityGranters"
<bean class="sample.TestAuthorityGranter"/>
</property>
<property name="callbackHandlers"
<list>
<bean class="org.acegisecurity.providers.jaas.JaasNameCallbackHandler"/>
<bean class="org.acegisecurity.providers.jaas.JaasPasswordCallbackHandler"/>
</property>
<property name="loginConfig" value="classpath:acegi.conf"/>
<property name="liginContextName" value="ACEGI"/>
</bean>
另外需要將JaasAuthenticationProvider添加到認(rèn)證管理器:
acegi.conf的內(nèi)容:
ACEGI {
sample.TestLoginModule required;
};
注釋:authorityGranters屬性能夠?yàn)橐呀?jīng)認(rèn)證用戶提供角色映射信息,由于這里的Jaas僅負(fù)責(zé)用戶認(rèn)證,而授權(quán)仍然被acegi接管。TestAuthorityGranter實(shí)現(xiàn)類:
package sample;
import java.security.Principal;
import java.util.HashSet;
import java.util.Set;
import org.acegisecurity.providers.jaas.AuthorityGranter;
/**
*
* @author worldheart
*
*/
public class TestAuthorityGranter implements AuthorityGranter {
public Set grant(Principal principal) {
Set<String> rtnSet = new HashSet<String>();
if (principal.getName().equals("TEST_PRINCIPAL")) {
rtnSet.add("ROLE_USER");
rtnSet.add("ROLE_ADMIN");
}
return rtnSet;
}
}
下面是TestLoginModel類:
package sample;
import java.security.Principal;
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.login.LoginException;
import javax.security.auth.spi.LoginModule;
/**
*
* @author worldheart
*
*/
public class TestLoginModule implements LoginModule {
private String user;
private String password;
private Subject subject;
public boolean abort() throws LoginException {
return true;
}
public boolean commit() throws LoginException {
return true;
}
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map sharedState, Map options) {
this.subject = subject;
try {
NameCallback nameCallback = new NameCallback("prompt");
PasswordCallback passwordCallback = new PasswordCallback("prompt",
false);
callbackHandler.handle(new Callback[] {nameCallback, passwordCallback });
user = nameCallback.getName();
password = new String(passwordCallback.getPassword());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public boolean login() throws LoginException {
if (!user.equals("marissa")) {
throw new LoginException("用戶名不對(duì)");
}
if (!password.equals("koala")) {
throw new LoginException("密碼不對(duì)");
}
subject.getPrincipals().add(new Principal() {
public String getName() {
return "TEST_PRINCIPAL";
}
});
subject.getPrincipals().add(new Principal() {
public String getName() {
return "NULL_PRINCIPAL";
}
});
return true;
}
public boolean logout() throws LoginException {
return true;
}
}