java安全 ——JAAS(Java 认证和授权服务)开发指南
                                                            生活随笔
收集整理的這篇文章主要介紹了
                                java安全 ——JAAS(Java 认证和授权服务)开发指南
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.                        
                                
                            
                            
                            以下內容轉自:?http://lyb520320.iteye.com/blog/720478
 
 
【0】README 1)本文翻譯自:http://java.sun.com/developer/technicalArticles/Security/jaasv2/?
2)傳統的JAVA安全機制沒有提供必要的架構支持傳統的認證和授權;在J2SE里的安全是基于公鑰密碼體系和代碼簽名。也就是說,認證是基于在JVM里執行代碼的思想,并且沒有對資源請求提供策略。而且授權也是基于這樣的概念--代碼試圖去使用一個計算機資源。Java認證和授權服務(JAAS)也就被設計成去應付這些缺點。?
3)JAAS使用基于用戶的的訪問控制增加了這個已經存在的基于代碼的訪問控制機制,也提高了認證能力。這樣你賦予的權限不僅是什么代碼在運行,而且可以是誰在運行它。這篇文章:?
  
傳統的操作系統(例如UNIX)通過多種challenge-response機制對主體或實體進行認證。用戶名和密碼組合是屬于最常見的了。這種技術也被用來使用HTTP基本認證方案保護資源。無論怎樣,challenge可以做得更復雜:例如,可以加密信息或者依賴與特定信息的持有者(比如媽咪的小名或者一個所選問題的答案)。然后基于這種challenge類型的響應必須是有效的。?
 
此外,大多數操作系統在一個實體或主體上的和資源列表上的基本授權是授予實體或主體去使用。例如,當一個用戶嘗試讀或者寫一個文件時,授權機制就會去驗證當前正在執行的主體是否有權限去訪問這個資源。?
 
5)JAAS概覽?
JAAS分為2個部分:認證(authentication)和授權(authorization). 也就是說,認證和授權兩個都可以使用。?
 
 
 
認證是基于Plugable Authentication Modules(PAMs)(可插入認證模型),它使用一個框架,用于客戶端和服務端。,認證部分是一個使用存在于J2SE中策略文件的認證方案的擴展。在一個可插入的方式中執行認證使Java應用程序可以去依賴于底層的安全機制。這樣做有個優點,就是新的或者修正的認證機制可以方便地插入,而不用對應用程序本身做修改。?
 
授權是一個基于存在的策略文件的機制的擴展,基于策略文件的機制被用作指定一個應用程序(或可執行代碼)能或者不能做什么操作。它是基于保護域的。也就是說,這種機制授權基于代碼來自哪里,而不是基于誰在執行代碼。用JAAS permissions或者訪問控制不僅能控制在運行什么代碼,也能控制誰正在運行它。?
注意:JAAS在J2SE 1.3.x是作為一個可選的包,但是到J2SE 1.4 已經被完全整合了。?
java.security.Policy API已經被更新了,能夠在策略文件里支持基于主體的查詢和基于主體的授權,等下你就可以看到?
 
---------------------------------------------------------------------------------------------------------------------
【1】使用JAAS認證(Authentication)?
0)客戶端通過一個LoginContext對象與JAAS相互作用,這個LoginContext對象提供了一種開發應用程序的方式,它不依賴于底層的認證技術。 1)LoginContext:是javax.security.auth.login包里的一個類,它描述了用于驗證對象(subjects)的方法。 2)Subject:就是在某個你想去認證和分配訪問權限的系統里的一個標識。一個主體(subject)可能是一個用戶、一個進程或者是一臺機器,它用javax.security.auth.Subject類表示。 3)java.security.Principal:?由于一個Subject可能涉及多個授權(一個網上銀行密碼和另一個電子郵件系統),java.security.Principal就被用作在那些關聯里的標識。也就是說,該Principal接口是一個能夠被用作代表某個實體、公司或者登陸ID的抽象概念。一個Subject可能包含多個Principles. 稍后將有一個示例類實現了這個Principal接口。?
4)LoginContext對象調用負責實現和執行認證的LoginModules。LoginModule接口(在javax.security.auth.spi 包里)必須讓認證技術的提供者去實現,并且能夠被應用程序指定提供一個特定認證類型。 4.1)LoginContext:?用來讀取Configuaration和實例化特定的LoginModules.?
4.2)Configuaration:?被某個特定的應用程序用作指定認證技術或者LoginModule。因此,不同的LoginModules能夠被應用到某個應用程序而不用對這個應用程序做任何的代碼修改。?
 
 
 
【2】JAAS認證的幾個荔枝
2.1)簡單代碼1展示了一個簡單的JAAS客戶端。 我已經使用了要調用LoginModules的LoginContext去執行認證,并且用"WeatherLogin"這個名字實例化了LoginContext, 還回調了"MyCallbackHandler"(實現見簡單代碼2)處理程序。這個名字在配置文件里將被用作索引,決定應該使用哪個LoginModule。 當你看這個配置文件(見簡單代碼5)后會變得更加清楚?;卣{處理程序傳給了底層的LoginModule,因此他們能夠通過提示用戶名/密碼同用戶進行交流和作用,例如:通過文本或者圖形用戶接口。一旦LoginContext已經被實例化了,login方法就被調用去登陸。?
 
簡單代碼1:MyClient.java?
package com.corejava.chapter9.authentication;import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException;// LoginContext:是javax.security.auth.login包里的一個類,它描述了用于驗證對象(subjects)的方法。 // LoginContext對象調用負責實現和執行認證的LoginModules。 public class MyClient {public static void main(String[] args){LoginContext context = null;try{// 在配置文件example.conf里,實體名"WeatherLogin"就是被MyClient.java用作關聯這個實體的名字。context = new LoginContext("WeatherLogin", new MyCallbackHandler());} catch (LoginException le){System.err.println("LoginContext cannot be created. "+ le.getMessage());System.exit(-1);} catch (SecurityException se){System.err.println("LoginContext cannot be created. "+ se.getMessage());}try{context.login();} catch (LoginException le){System.out.println("Authentication failed. " + le.getMessage());System.exit(-1);}System.out.println("authentication succeeded.");System.exit(-1);} }<strong> </strong>
一個基于JAAS的應用程序實現了CallbackHandler接口,因此它能夠提示用戶去輸入特定的認證信息,比如用戶名或者密碼,或者顯示錯誤或者警告信息。底層安全服務可能要求通過傳遞單個的callbacks到回調處理程序?;趥鬟f的callbacks,回調處理程序決定怎樣去獲取和顯示信息。例如,如果底層服務需要一個用戶名和密碼認證一個用戶,它可以使用NameCallback和PasswordCallback. 其他的callbacks類都在javax.security.auth.callback里,包括:?
 
 
實現CallbackHandler接口意味著你需要實現handler方法,以獲取或者顯示在提供的callbacks里要求的信息。簡單代碼2是一個簡單的實現。注意在這里我使用NameCallback與用戶關聯。?
 
簡單代碼2:MyCallbackHandler.java?
?package com.corejava.chapter9.authentication;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.UnsupportedCallbackException;// 一個基于JAAS的應用程序實現了CallbackHandler接口, // 因此它能夠提示用戶去輸入特定的認證信息,比如用戶名或者密碼,或者顯示錯誤或者警告信息。 // 基于傳遞的callbacks,回調處理程序決定怎樣去獲取和顯示信息。 public class MyCallbackHandler implements CallbackHandler {@Overridepublic void handle(Callback[] callbacks) throws IOException,UnsupportedCallbackException{for (int i = 0; i < callbacks.length; i++){if(callbacks[i] instanceof NameCallback){NameCallback nc = (NameCallback)callbacks[0];System.err.println(nc.getPrompt());System.err.flush();String name = (new BufferedReader(new InputStreamReader(System.in))).readLine();nc.setName(name);}else{throw new UnsupportedCallbackException(callbacks[i], "callback handler not support");}}}}<strong> </strong>
現在,我們看一個LoginModule的簡單實現。注意在真實的應用程序開發中,我們不必要自己去實現LoginMoudule。我們可以使用第三方的login模型并把他們應用到我們的程序。例如,Sum Microsystems 提供了幾個LoginModule,包括:JndiLoginModule、KeyStoreLoginModule、Krb5LoginModule、NTLoginModule、UNIXLoginModule. 如果你愿意學習怎樣去使用這些login模型,請查看本文的參考信息部分。?(干貨——第三方的登錄模型-LoginModule)
無論怎樣,簡單代碼3展示了一個LoginModule簡單的實現. 這個例子是非常簡單的,因為他僅僅有一個認證字符串和一個Principal "SunnyDay", 兩個都是硬編碼。如果去login,系統將顯示"What is the weather like today?", 如果答案是"Sunny", 用戶就能通過。 Attention)注意MyCallbackHandler是怎樣被用在login方法里的。除了login方法,你必須實現其他四個方法:initialize, commit, abort, and logout. 這些方法將被LoginContext用在接下來的流程里。?(干貨——Principal是特征)
 
簡單代碼3:WeatherLoginModule.java?
package com.corejava.chapter9.authentication;import java.util.Map; import java.util.Set;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.UnsupportedCallbackException; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule;// 以下代碼展示了一個LoginModule簡單的實現. 這個例子是非常簡單的, // 因為他僅僅有一個認證字符串和一個Principal(特征) "SunnyDay", 兩個都是硬編碼。 // 如果去login,系統將顯示"What is the weather like today?", 如果答案是"Sunny", 用戶就能通過。 public class WeatherLoginModule implements LoginModule {private Subject subject;private ExamplePrincipal entity;private CallbackHandler callbackhandler;private static final int NOT = 0;private static final int OK = 1;private static final int COMMIT = 2;private int status;// initialize: 這個方法的目的就是用有關的信息去實例化這個LoginModule。// 如果login成功,在這個方法里的Subject就被用在存儲Principals和Credentials. // 注意這個方法有一個能被用作輸入認證信息的CallbackHandler。在這個例子里,我沒有用CallbackHandler. // CallbackHandler是有用的,因為它從被用作特定輸入設備里分離了服務提供者。public void initialize(Subject subject, CallbackHandlercallbackhandler, Map state, Map options){status = NOT;entity = null;this.subject = subject;this.callbackhandler = callbackhandler;}// login: 請求LoginModule去認證Subject. 注意此時Principal還沒有被指定。public boolean login() throws LoginException{if (callbackhandler == null){throw new LoginException("No callback handler is available");}Callback callbacks[] = new Callback[1];callbacks[0] = new NameCallback("What is the weather like today?");String name = null;try{// 調用 MyCallbackHandler.java 中的 handle 方法進行處理// 以讀入用戶輸入的認證信息(如 username)callbackhandler.handle(callbacks); name = ((NameCallback) callbacks[0]).getName();} catch (java.io.IOException ioe){throw new LoginException(ioe.toString());} catch (UnsupportedCallbackException ce){throw new LoginException("Error: " + ce.getCallback().toString());}if (name.equals("Sunny")){entity = new ExamplePrincipal("SunnyDay");status = OK;return true;} else{return false;}}// commit: 如果LoginContext的認證全部成功就調用這個方法。public boolean commit() throws LoginException{if (status == NOT){return false;}if (subject == null){return false;}Set entities = subject.getPrincipals();if (!entities.contains(entity)){entities.add(entity);}status = COMMIT;return true;}// abort: 通知其他LoginModule供應者或LoginModule模型認證已經失敗了。整個login將失敗。public boolean abort() throws LoginException{if ((subject != null) && (entity != null)){Set entities = subject.getPrincipals();if (entities.contains(entity)){entities.remove(entity);}}subject = null;entity = null;status = NOT;return true;}// logout: 通過從Subject里移除Principals和Credentials注銷Subject。public boolean logout() throws LoginException{subject.getPrincipals().remove(entity);status = NOT;subject = null;return true;}}<strong> </strong>
正如你從簡單代碼3里所看到的,一個ExamplePrincipal類正在被使用。這個類是Principal接口的一個實現。?
簡單代碼4展示了Principal接口的一個實現。?
簡單代碼4:ExamplePrincipal.java ?? package com.corejava.chapter9.authentication;import java.security.Principal;// ExamplePrincipal 展示了Principal(主體特征)接口的一個實現。 public class ExamplePrincipal implements Principal {private final String name;public ExamplePrincipal(String name){if (name == null){throw new IllegalArgumentException("Null name");}this.name = name;}public String getName(){return name;}public String toString(){return "ExamplePrinciapl: " + name;}public boolean equals(Object obj){if (obj == null)return false;if (obj == this)return true;if (!(obj instanceof ExamplePrincipal))return false;ExamplePrincipal another = (ExamplePrincipal) obj;return name.equals(another.getName());}public int hasCode(){return name.hashCode();} }
正如我前面提及的,LoginContext通過讀取Configuration去決定那個LoginModule將被使用。login配置文件可以是一個文件或者是數據庫。當前Sun Microsystems默認的實現是一個文件。一個login配置文件包含一個或者多個實體,這些實體指出了那些認證技術應該被擁有應用程序。簡單代碼5展示了一個login配置文件。?
 
簡單代碼5:example.conf ? WeatherLogin {com.corejava.chapter9.authentication.WeatherLoginModule required; };
在這個配置文件里,實體名"WeatherLogin"就是被MyClient.java用作關聯這個實體的名字。這里的這個實體指出WeatherLoginModule應該被用作執行認證。為了使整個認證成功,這個模型(module)的認證必須是成功的。用戶輸入了正確的信息就是成功的。?
 
運行例子程序?
 
 在根目錄下創建一個"auth"的目錄; 把MyClient.java, WeatherLoginModule.java, ExamplePrincipal.java, example.conf復制到這個目錄下; 編譯所有的java文件:javac *.java; 使用下面的命令(指定了login配置文件)運行客戶端,??? prompt> java -Djava.security.auth.login.config=example.conf MyClient?
??
??? 你應該可以看到下面的輸出(粗體文本是用戶輸入的)。?
 
????? What is the weather like today??gloomy?
???? Authentication failed. Login Failure: all modules ignored?
 
??? 在用正確的輸入運行客戶端:?
 
???? What is the weather like today??Sunny?
???? Authentication succeeded?
   
 
 
 
 
Attention) A1)個人以為:登錄模型-LoginModule 中定義的5個方法是干貨;
 
 
 
【3】安全管理器?
在上面的例子里,默認是沒有運行在安全管理器下的,因此所有操作都是允許的。為了保護資源,可以使用下面的命令去運行在安全管理器下:?
 
Java代碼??C:\sun\code\auth>java?-Djava.security.manager//?? ?-Djava.security.auth.login.config=example.conf?MyClient?? LoginContext?cannot?be?created.?access?denied//?? (javax.security.auth.AuthPermission?createLoginContext.WeatherLogin)?? Exception?in?thread?"main"?java.lang.NullPointerException?? ????????at?MyClient.main(MyClient.java:17)??   
你可以看到拋出了一個異常。默認的安全管理器不允許任何操作,因此login上下文沒有被創建。要允許這些操作,你必須創建一個安全策略(安全策略是一個賦予代碼能不能執行的權限的文本文件。簡單代碼6展示了一個簡單的安全策略。目標createLoginContext使MyClient能夠實例化一個login上下文。目標modifyPrincipals允許WeatherLoginModule用一個Principal去構造一個Subject.?
 
簡單代碼6:policy.txt?
grant codebase "file:./*" { permission javax.security.auth.AuthPermission "createLoginContext"; permission javax.security.auth.AuthPermission "modifyPrincipals"; };
現在你就能夠使用下面的命令來運行了,注意雙等號(==)是用來覆蓋默認安全策略的。? prompt>java -Djava.security.manager -Djava.security.policy==policy.txt?
-Djava.security.auth.login.config==example.conf MyClient?
 
What is the weather like today??Sunny?
Authentication succeeded?
? ?Attention) A1)LoginModule的實現代碼和應用程序代碼也可以放在一個jar文件里。更多相關信息,請看LoginModule Developer's Guide.?
 
 
A2) 我的Authentication 實例涉及到的目錄結構
A3) for complete source code of jaas authentication, please visit?https://github.com/pacosonTang/core-java-volume/tree/master/coreJavaAdvanced/chapter9/authentication?
 
 
  
 
【4】使用JAAS授權(Authorization)?(授權的對象必須是經過JAAS 認證通過的對象)
4.1)JAAS授權繼承了以代碼為中心的JAVA安全體系結構(它使用一個安全策略指定什么樣的訪問權限授予執行中的代碼。例如,在簡單代碼6的安全策略里,所有當前目錄的的代碼都被授權;不管代碼有沒有簽名,或者誰在運行這些代碼。JAAS也繼承了以用戶為中心(user-centric)的訪問控制。許可權的賦予不僅是正在運行什么代碼,而且也看誰在運行它。很快你將看到許可權能夠在策略文件里被賦予去指定principals。?
 
4.2)為了使用JAAS授權:?
 用戶必須是我在上面認證過的; Subject的doAs(或者doAsPrivileged)方法必須被調用,它在反過來調用run方法(包含作為主體去執行的)。  基于這個,ExamplePrincipal.java, WeatherLoginModule.java和 example.conf文件仍然是同一個。?
 
為了使客戶端能夠賦予用戶權限,一旦認證成功(用戶已經被認證了),認證了的主體就使用通過Subject subject=ctx.getSubject()來獲取。然后通過傳遞給Subject.doAsPrivileged()一個認證了的主體和一個特權行動以及空AccessControlContext去調用它。這些改變在簡單代碼7是用高亮標識的。注意一旦執行了認證過程,我就調用ctx.logout是用戶退出。?
 
簡單代碼7: MyClient.java?
package com.corejava.chapter9.authorization;import java.security.PrivilegedAction;import javax.security.auth.Subject; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException;import com.corejava.chapter9.authentication.MyCallbackHandler;public class MyClient {public static void main(String argv[]) { LoginContext ctx = null; try { ctx = new LoginContext("WeatherLogin", new MyCallbackHandler()); } catch(LoginException le) { System.err.println("LoginContext cannot be created. "+ le.getMessage()); System.exit(-1); } catch(SecurityException se) { System.err.println("LoginContext cannot be created. "+ se.getMessage()); }try { ctx.login(); } catch(LoginException le) { System.out.println("Authentication failed"); System.exit(-1); } System.out.println("Authentication succeeded"); // 為了使客戶端能夠賦予用戶權限,一旦認證成功(用戶已經被認證了),// 認證了的主體就使用通過Subject subject=ctx.getSubject()來獲取。Subject subject = ctx.getSubject(); System.out.println(subject.toString());PrivilegedAction action = new MyAction(); // Subject的doAs(或者doAsPrivileged)方法必須被調用,它在反過來調用run方法(包含作為主體去執行的)。Subject.doAsPrivileged(subject, action, null); try { ctx.logout(); } catch(LoginException le) { System.out.println("Logout: " + le.getMessage()); } } }<strong> </strong>
你可以看到,我們傳遞了一個action給doAsPrivileged,action就是用戶被授權去執行的行為。簡單代碼8展示了MyAction.java, 它通過提供run方法的代碼實現了PrivilegedAction接口,run方法包含了所有將用基于principal的認證檢查的代碼。現在,當執行doAsPrivileged方法時,它就會反過來調用在PrivilegedAction里的方法,去實例化主體其他方面剩余代碼的執行。?
在這個例子里,action就是去檢查文件"test.txt"存在于當前的目錄。?
 
簡單代碼8:MyAction.java?
package com.corejava.chapter9.authorization;import java.io.File; import java.security.PrivilegedAction;public class MyAction implements PrivilegedAction {@Overridepublic Object run(){System.out.println("this is the 1st authorization task after passing authentication"); File file = new File("com/corejava/chapter9/authorization/test.txt"); if(file.exists()) { System.out.println("The file exists in the current working directory"); }else { System.out.println("The file does not exist in the current working directory"); } return null; } }<strong> </strong>
現在我需要更新policy.txt文件。簡單代碼9展示了這些更改。?
 
 為了Subject類的doAsPrivileged方法,你需要有一個有doAsPrivileged目標的java.security.auth.AuthPermission. 添加了一個基于principal的授權;它聲明了一個"SunnyDay"的pincipal被運行去讀這個"test.txt"文件。  
簡單代碼9:policy.txt?
grant codebase "file:./*" { permission javax.security.auth.AuthPermission "createLoginContext"; permission javax.security.auth.AuthPermission "modifyPrincipals"; permission javax.security.auth.AuthPermission "doAsPrivileged"; }; grant codebase "file:./*", Principal ExamplePrincipal "SunnyDay" { permission java.io.FilePermission "com/corejava/chapter9/authorization/test.txt", "read"; }; ?? 現在當你運行這個應用程序,它將首先執行認證。如果認證失敗,應用程序就停止,否則它將繼續去檢查這個文件。看下面簡單的運行:?
prompt>java -Djava.security.manager -Djava.security.policy==policy.txt//?
-Djava.security.auth.login.config==example.conf MyClient?
What is the weather like today??sunny?
Authentication failed?
 
prompt>java -Djava.security.manager -Djava.security.policy==policy.txt//?
-Djava.security.auth.login.config==example.conf MyClient?
What is the weather like today??Sunny?
Authentication succeeded?
The file does not exist in the current working directory?
 
E:\bench-cluster\cloud-data-preprocess\CoreJavaAdvanced\src>java -Djava.security.manager -Djava.secu rity.policy==com/corejava/chapter9/authorization/policy.txt -Djava.security.auth.login.config==com/c orejava/chapter9/authorization/example.conf com.corejava.chapter9.authorization.MyClient
 
Attention) A1)? 我的Authorization 實例涉及到的目錄結構
A2)?for complete source code of jaas authentication, please visit ??https://github.com/pacosonTang/core-java-volume/tree/master/coreJavaAdvanced/chapter9/authorization?
 
 
結束語?
 
JAAS是一個能夠認證和強制執行用戶訪問控制的服務的API集合。JAAS認證使用可插入的方式,它不依賴與底層的認證技術;JAAS授權使用以用戶為中心的訪問控制和認證能力提高了以原本以代碼為中心的JAVA安全體系。
 
                            
                        
                        
                        【0】README 1)本文翻譯自:http://java.sun.com/developer/technicalArticles/Security/jaasv2/?
2)傳統的JAVA安全機制沒有提供必要的架構支持傳統的認證和授權;在J2SE里的安全是基于公鑰密碼體系和代碼簽名。也就是說,認證是基于在JVM里執行代碼的思想,并且沒有對資源請求提供策略。而且授權也是基于這樣的概念--代碼試圖去使用一個計算機資源。Java認證和授權服務(JAAS)也就被設計成去應付這些缺點。?
3)JAAS使用基于用戶的的訪問控制增加了這個已經存在的基于代碼的訪問控制機制,也提高了認證能力。這樣你賦予的權限不僅是什么代碼在運行,而且可以是誰在運行它。這篇文章:?
- 討論基于代碼的認證的缺點
- 提供JAAS的概覽
- 展示了一個加強的代碼的認證例子
- 展示了一個加強的代碼的授權例子
- 給予了在開發認證和授權服務時調用成功的感覺。
- 提供了能夠適應與你所擁有的應用程序的簡單代碼
- 提供了一個開始學習JAAS的指導
傳統的操作系統(例如UNIX)通過多種challenge-response機制對主體或實體進行認證。用戶名和密碼組合是屬于最常見的了。這種技術也被用來使用HTTP基本認證方案保護資源。無論怎樣,challenge可以做得更復雜:例如,可以加密信息或者依賴與特定信息的持有者(比如媽咪的小名或者一個所選問題的答案)。然后基于這種challenge類型的響應必須是有效的。?
此外,大多數操作系統在一個實體或主體上的和資源列表上的基本授權是授予實體或主體去使用。例如,當一個用戶嘗試讀或者寫一個文件時,授權機制就會去驗證當前正在執行的主體是否有權限去訪問這個資源。?
5)JAAS概覽?
JAAS分為2個部分:認證(authentication)和授權(authorization). 也就是說,認證和授權兩個都可以使用。?
- 對于認證用戶可以安全地決定誰正在執行代碼,即使代碼是一個單獨的應用程序、一個applet、一個企業級JavaBean或者一個servlet。
- 對于用戶的授權,可以確認他們是否有必要的權限去執行他們的操作。
認證是基于Plugable Authentication Modules(PAMs)(可插入認證模型),它使用一個框架,用于客戶端和服務端。,認證部分是一個使用存在于J2SE中策略文件的認證方案的擴展。在一個可插入的方式中執行認證使Java應用程序可以去依賴于底層的安全機制。這樣做有個優點,就是新的或者修正的認證機制可以方便地插入,而不用對應用程序本身做修改。?
授權是一個基于存在的策略文件的機制的擴展,基于策略文件的機制被用作指定一個應用程序(或可執行代碼)能或者不能做什么操作。它是基于保護域的。也就是說,這種機制授權基于代碼來自哪里,而不是基于誰在執行代碼。用JAAS permissions或者訪問控制不僅能控制在運行什么代碼,也能控制誰正在運行它。?
注意:JAAS在J2SE 1.3.x是作為一個可選的包,但是到J2SE 1.4 已經被完全整合了。?
java.security.Policy API已經被更新了,能夠在策略文件里支持基于主體的查詢和基于主體的授權,等下你就可以看到?
---------------------------------------------------------------------------------------------------------------------
【1】使用JAAS認證(Authentication)?
0)客戶端通過一個LoginContext對象與JAAS相互作用,這個LoginContext對象提供了一種開發應用程序的方式,它不依賴于底層的認證技術。 1)LoginContext:是javax.security.auth.login包里的一個類,它描述了用于驗證對象(subjects)的方法。 2)Subject:就是在某個你想去認證和分配訪問權限的系統里的一個標識。一個主體(subject)可能是一個用戶、一個進程或者是一臺機器,它用javax.security.auth.Subject類表示。 3)java.security.Principal:?由于一個Subject可能涉及多個授權(一個網上銀行密碼和另一個電子郵件系統),java.security.Principal就被用作在那些關聯里的標識。也就是說,該Principal接口是一個能夠被用作代表某個實體、公司或者登陸ID的抽象概念。一個Subject可能包含多個Principles. 稍后將有一個示例類實現了這個Principal接口。?
4)LoginContext對象調用負責實現和執行認證的LoginModules。LoginModule接口(在javax.security.auth.spi 包里)必須讓認證技術的提供者去實現,并且能夠被應用程序指定提供一個特定認證類型。 4.1)LoginContext:?用來讀取Configuaration和實例化特定的LoginModules.?
4.2)Configuaration:?被某個特定的應用程序用作指定認證技術或者LoginModule。因此,不同的LoginModules能夠被應用到某個應用程序而不用對這個應用程序做任何的代碼修改。?
【2】JAAS認證的幾個荔枝
2.1)簡單代碼1展示了一個簡單的JAAS客戶端。 我已經使用了要調用LoginModules的LoginContext去執行認證,并且用"WeatherLogin"這個名字實例化了LoginContext, 還回調了"MyCallbackHandler"(實現見簡單代碼2)處理程序。這個名字在配置文件里將被用作索引,決定應該使用哪個LoginModule。 當你看這個配置文件(見簡單代碼5)后會變得更加清楚?;卣{處理程序傳給了底層的LoginModule,因此他們能夠通過提示用戶名/密碼同用戶進行交流和作用,例如:通過文本或者圖形用戶接口。一旦LoginContext已經被實例化了,login方法就被調用去登陸。?
簡單代碼1:MyClient.java?
package com.corejava.chapter9.authentication;import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException;// LoginContext:是javax.security.auth.login包里的一個類,它描述了用于驗證對象(subjects)的方法。 // LoginContext對象調用負責實現和執行認證的LoginModules。 public class MyClient {public static void main(String[] args){LoginContext context = null;try{// 在配置文件example.conf里,實體名"WeatherLogin"就是被MyClient.java用作關聯這個實體的名字。context = new LoginContext("WeatherLogin", new MyCallbackHandler());} catch (LoginException le){System.err.println("LoginContext cannot be created. "+ le.getMessage());System.exit(-1);} catch (SecurityException se){System.err.println("LoginContext cannot be created. "+ se.getMessage());}try{context.login();} catch (LoginException le){System.out.println("Authentication failed. " + le.getMessage());System.exit(-1);}System.out.println("authentication succeeded.");System.exit(-1);} }<strong> </strong>
一個基于JAAS的應用程序實現了CallbackHandler接口,因此它能夠提示用戶去輸入特定的認證信息,比如用戶名或者密碼,或者顯示錯誤或者警告信息。底層安全服務可能要求通過傳遞單個的callbacks到回調處理程序?;趥鬟f的callbacks,回調處理程序決定怎樣去獲取和顯示信息。例如,如果底層服務需要一個用戶名和密碼認證一個用戶,它可以使用NameCallback和PasswordCallback. 其他的callbacks類都在javax.security.auth.callback里,包括:?
- ChoiceCallback (顯示一個選擇列表)
- ConfirmationCallback (詢問 YES/NO, OK/CANCEL)
- LanguageCallback (用作區域化的Locale)
- TextInputCallback (獲取普通的文本信息)
- TextOutputCallback (顯示信息,警告和錯誤信息)
實現CallbackHandler接口意味著你需要實現handler方法,以獲取或者顯示在提供的callbacks里要求的信息。簡單代碼2是一個簡單的實現。注意在這里我使用NameCallback與用戶關聯。?
簡單代碼2:MyCallbackHandler.java?
?package com.corejava.chapter9.authentication;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.UnsupportedCallbackException;// 一個基于JAAS的應用程序實現了CallbackHandler接口, // 因此它能夠提示用戶去輸入特定的認證信息,比如用戶名或者密碼,或者顯示錯誤或者警告信息。 // 基于傳遞的callbacks,回調處理程序決定怎樣去獲取和顯示信息。 public class MyCallbackHandler implements CallbackHandler {@Overridepublic void handle(Callback[] callbacks) throws IOException,UnsupportedCallbackException{for (int i = 0; i < callbacks.length; i++){if(callbacks[i] instanceof NameCallback){NameCallback nc = (NameCallback)callbacks[0];System.err.println(nc.getPrompt());System.err.flush();String name = (new BufferedReader(new InputStreamReader(System.in))).readLine();nc.setName(name);}else{throw new UnsupportedCallbackException(callbacks[i], "callback handler not support");}}}}<strong> </strong>
現在,我們看一個LoginModule的簡單實現。注意在真實的應用程序開發中,我們不必要自己去實現LoginMoudule。我們可以使用第三方的login模型并把他們應用到我們的程序。例如,Sum Microsystems 提供了幾個LoginModule,包括:JndiLoginModule、KeyStoreLoginModule、Krb5LoginModule、NTLoginModule、UNIXLoginModule. 如果你愿意學習怎樣去使用這些login模型,請查看本文的參考信息部分。?(干貨——第三方的登錄模型-LoginModule)
無論怎樣,簡單代碼3展示了一個LoginModule簡單的實現. 這個例子是非常簡單的,因為他僅僅有一個認證字符串和一個Principal "SunnyDay", 兩個都是硬編碼。如果去login,系統將顯示"What is the weather like today?", 如果答案是"Sunny", 用戶就能通過。 Attention)注意MyCallbackHandler是怎樣被用在login方法里的。除了login方法,你必須實現其他四個方法:initialize, commit, abort, and logout. 這些方法將被LoginContext用在接下來的流程里。?(干貨——Principal是特征)
- initialize: 這個方法的目的就是用有關的信息去實例化這個LoginModule。如果login成功,在這個方法里的Subject就被用在存儲Principals和Credentials.? 注意這個方法有一個能被用作輸入認證信息的CallbackHandler。在這個例子里,我沒有用CallbackHandler. CallbackHandler是有用的,因為它從被用作特定輸入設備里分離了服務提供者。
- login: 請求LoginModule去認證Subject. 注意此時Principal還沒有被指定。
- commit: 如果LoginContext的認證全部成功就調用這個方法。
- abort: 通知其他LoginModule供應者或LoginModule模型認證已經失敗了。整個login將失敗。
- logout: 通過從Subject里移除Principals和Credentials注銷Subject。
簡單代碼3:WeatherLoginModule.java?
package com.corejava.chapter9.authentication;import java.util.Map; import java.util.Set;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.UnsupportedCallbackException; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule;// 以下代碼展示了一個LoginModule簡單的實現. 這個例子是非常簡單的, // 因為他僅僅有一個認證字符串和一個Principal(特征) "SunnyDay", 兩個都是硬編碼。 // 如果去login,系統將顯示"What is the weather like today?", 如果答案是"Sunny", 用戶就能通過。 public class WeatherLoginModule implements LoginModule {private Subject subject;private ExamplePrincipal entity;private CallbackHandler callbackhandler;private static final int NOT = 0;private static final int OK = 1;private static final int COMMIT = 2;private int status;// initialize: 這個方法的目的就是用有關的信息去實例化這個LoginModule。// 如果login成功,在這個方法里的Subject就被用在存儲Principals和Credentials. // 注意這個方法有一個能被用作輸入認證信息的CallbackHandler。在這個例子里,我沒有用CallbackHandler. // CallbackHandler是有用的,因為它從被用作特定輸入設備里分離了服務提供者。public void initialize(Subject subject, CallbackHandlercallbackhandler, Map state, Map options){status = NOT;entity = null;this.subject = subject;this.callbackhandler = callbackhandler;}// login: 請求LoginModule去認證Subject. 注意此時Principal還沒有被指定。public boolean login() throws LoginException{if (callbackhandler == null){throw new LoginException("No callback handler is available");}Callback callbacks[] = new Callback[1];callbacks[0] = new NameCallback("What is the weather like today?");String name = null;try{// 調用 MyCallbackHandler.java 中的 handle 方法進行處理// 以讀入用戶輸入的認證信息(如 username)callbackhandler.handle(callbacks); name = ((NameCallback) callbacks[0]).getName();} catch (java.io.IOException ioe){throw new LoginException(ioe.toString());} catch (UnsupportedCallbackException ce){throw new LoginException("Error: " + ce.getCallback().toString());}if (name.equals("Sunny")){entity = new ExamplePrincipal("SunnyDay");status = OK;return true;} else{return false;}}// commit: 如果LoginContext的認證全部成功就調用這個方法。public boolean commit() throws LoginException{if (status == NOT){return false;}if (subject == null){return false;}Set entities = subject.getPrincipals();if (!entities.contains(entity)){entities.add(entity);}status = COMMIT;return true;}// abort: 通知其他LoginModule供應者或LoginModule模型認證已經失敗了。整個login將失敗。public boolean abort() throws LoginException{if ((subject != null) && (entity != null)){Set entities = subject.getPrincipals();if (entities.contains(entity)){entities.remove(entity);}}subject = null;entity = null;status = NOT;return true;}// logout: 通過從Subject里移除Principals和Credentials注銷Subject。public boolean logout() throws LoginException{subject.getPrincipals().remove(entity);status = NOT;subject = null;return true;}}<strong> </strong>
正如你從簡單代碼3里所看到的,一個ExamplePrincipal類正在被使用。這個類是Principal接口的一個實現。?
簡單代碼4展示了Principal接口的一個實現。?
簡單代碼4:ExamplePrincipal.java ?? package com.corejava.chapter9.authentication;import java.security.Principal;// ExamplePrincipal 展示了Principal(主體特征)接口的一個實現。 public class ExamplePrincipal implements Principal {private final String name;public ExamplePrincipal(String name){if (name == null){throw new IllegalArgumentException("Null name");}this.name = name;}public String getName(){return name;}public String toString(){return "ExamplePrinciapl: " + name;}public boolean equals(Object obj){if (obj == null)return false;if (obj == this)return true;if (!(obj instanceof ExamplePrincipal))return false;ExamplePrincipal another = (ExamplePrincipal) obj;return name.equals(another.getName());}public int hasCode(){return name.hashCode();} }
正如我前面提及的,LoginContext通過讀取Configuration去決定那個LoginModule將被使用。login配置文件可以是一個文件或者是數據庫。當前Sun Microsystems默認的實現是一個文件。一個login配置文件包含一個或者多個實體,這些實體指出了那些認證技術應該被擁有應用程序。簡單代碼5展示了一個login配置文件。?
簡單代碼5:example.conf ? WeatherLogin {com.corejava.chapter9.authentication.WeatherLoginModule required; };
在這個配置文件里,實體名"WeatherLogin"就是被MyClient.java用作關聯這個實體的名字。這里的這個實體指出WeatherLoginModule應該被用作執行認證。為了使整個認證成功,這個模型(module)的認證必須是成功的。用戶輸入了正確的信息就是成功的。?
運行例子程序?
??
??? 你應該可以看到下面的輸出(粗體文本是用戶輸入的)。?
????? What is the weather like today??gloomy?
???? Authentication failed. Login Failure: all modules ignored?
??? 在用正確的輸入運行客戶端:?
???? What is the weather like today??Sunny?
???? Authentication succeeded?
Attention) A1)個人以為:登錄模型-LoginModule 中定義的5個方法是干貨;
- initialize: 這個方法的目的就是用有關的信息去實例化這個LoginModule。如果login成功,在這個方法里的Subject就被用在存儲Principals和Credentials.? 注意這個方法有一個能被用作輸入認證信息的CallbackHandler。在這個例子里,我沒有用CallbackHandler. CallbackHandler是有用的,因為它從被用作特定輸入設備里分離了服務提供者。
- login: 請求LoginModule去認證Subject. 注意此時Principal還沒有被指定。
- commit: 如果LoginContext的認證全部成功就調用這個方法。
- abort: 通知其他LoginModule供應者或LoginModule模型認證已經失敗了。整個login將失敗。
- logout: 通過從Subject里移除Principals和Credentials注銷Subject。
| UnixLoginModule NTLoginModule Krb5LoginModule JndiLoginModule KeyStoreLoginModule | 
【3】安全管理器?
在上面的例子里,默認是沒有運行在安全管理器下的,因此所有操作都是允許的。為了保護資源,可以使用下面的命令去運行在安全管理器下:?
Java代碼??
你可以看到拋出了一個異常。默認的安全管理器不允許任何操作,因此login上下文沒有被創建。要允許這些操作,你必須創建一個安全策略(安全策略是一個賦予代碼能不能執行的權限的文本文件。簡單代碼6展示了一個簡單的安全策略。目標createLoginContext使MyClient能夠實例化一個login上下文。目標modifyPrincipals允許WeatherLoginModule用一個Principal去構造一個Subject.?
簡單代碼6:policy.txt?
grant codebase "file:./*" { permission javax.security.auth.AuthPermission "createLoginContext"; permission javax.security.auth.AuthPermission "modifyPrincipals"; };
現在你就能夠使用下面的命令來運行了,注意雙等號(==)是用來覆蓋默認安全策略的。? prompt>java -Djava.security.manager -Djava.security.policy==policy.txt?
-Djava.security.auth.login.config==example.conf MyClient?
What is the weather like today??Sunny?
Authentication succeeded?
? ?Attention) A1)LoginModule的實現代碼和應用程序代碼也可以放在一個jar文件里。更多相關信息,請看LoginModule Developer's Guide.?
A2) 我的Authentication 實例涉及到的目錄結構
A3) for complete source code of jaas authentication, please visit?https://github.com/pacosonTang/core-java-volume/tree/master/coreJavaAdvanced/chapter9/authentication?
【4】使用JAAS授權(Authorization)?(授權的對象必須是經過JAAS 認證通過的對象)
4.1)JAAS授權繼承了以代碼為中心的JAVA安全體系結構(它使用一個安全策略指定什么樣的訪問權限授予執行中的代碼。例如,在簡單代碼6的安全策略里,所有當前目錄的的代碼都被授權;不管代碼有沒有簽名,或者誰在運行這些代碼。JAAS也繼承了以用戶為中心(user-centric)的訪問控制。許可權的賦予不僅是正在運行什么代碼,而且也看誰在運行它。很快你將看到許可權能夠在策略文件里被賦予去指定principals。?
4.2)為了使用JAAS授權:?
為了使客戶端能夠賦予用戶權限,一旦認證成功(用戶已經被認證了),認證了的主體就使用通過Subject subject=ctx.getSubject()來獲取。然后通過傳遞給Subject.doAsPrivileged()一個認證了的主體和一個特權行動以及空AccessControlContext去調用它。這些改變在簡單代碼7是用高亮標識的。注意一旦執行了認證過程,我就調用ctx.logout是用戶退出。?
簡單代碼7: MyClient.java?
package com.corejava.chapter9.authorization;import java.security.PrivilegedAction;import javax.security.auth.Subject; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException;import com.corejava.chapter9.authentication.MyCallbackHandler;public class MyClient {public static void main(String argv[]) { LoginContext ctx = null; try { ctx = new LoginContext("WeatherLogin", new MyCallbackHandler()); } catch(LoginException le) { System.err.println("LoginContext cannot be created. "+ le.getMessage()); System.exit(-1); } catch(SecurityException se) { System.err.println("LoginContext cannot be created. "+ se.getMessage()); }try { ctx.login(); } catch(LoginException le) { System.out.println("Authentication failed"); System.exit(-1); } System.out.println("Authentication succeeded"); // 為了使客戶端能夠賦予用戶權限,一旦認證成功(用戶已經被認證了),// 認證了的主體就使用通過Subject subject=ctx.getSubject()來獲取。Subject subject = ctx.getSubject(); System.out.println(subject.toString());PrivilegedAction action = new MyAction(); // Subject的doAs(或者doAsPrivileged)方法必須被調用,它在反過來調用run方法(包含作為主體去執行的)。Subject.doAsPrivileged(subject, action, null); try { ctx.logout(); } catch(LoginException le) { System.out.println("Logout: " + le.getMessage()); } } }<strong> </strong>
你可以看到,我們傳遞了一個action給doAsPrivileged,action就是用戶被授權去執行的行為。簡單代碼8展示了MyAction.java, 它通過提供run方法的代碼實現了PrivilegedAction接口,run方法包含了所有將用基于principal的認證檢查的代碼。現在,當執行doAsPrivileged方法時,它就會反過來調用在PrivilegedAction里的方法,去實例化主體其他方面剩余代碼的執行。?
在這個例子里,action就是去檢查文件"test.txt"存在于當前的目錄。?
簡單代碼8:MyAction.java?
package com.corejava.chapter9.authorization;import java.io.File; import java.security.PrivilegedAction;public class MyAction implements PrivilegedAction {@Overridepublic Object run(){System.out.println("this is the 1st authorization task after passing authentication"); File file = new File("com/corejava/chapter9/authorization/test.txt"); if(file.exists()) { System.out.println("The file exists in the current working directory"); }else { System.out.println("The file does not exist in the current working directory"); } return null; } }<strong> </strong>
現在我需要更新policy.txt文件。簡單代碼9展示了這些更改。?
簡單代碼9:policy.txt?
grant codebase "file:./*" { permission javax.security.auth.AuthPermission "createLoginContext"; permission javax.security.auth.AuthPermission "modifyPrincipals"; permission javax.security.auth.AuthPermission "doAsPrivileged"; }; grant codebase "file:./*", Principal ExamplePrincipal "SunnyDay" { permission java.io.FilePermission "com/corejava/chapter9/authorization/test.txt", "read"; }; ?? 現在當你運行這個應用程序,它將首先執行認證。如果認證失敗,應用程序就停止,否則它將繼續去檢查這個文件。看下面簡單的運行:?
prompt>java -Djava.security.manager -Djava.security.policy==policy.txt//?
-Djava.security.auth.login.config==example.conf MyClient?
What is the weather like today??sunny?
Authentication failed?
prompt>java -Djava.security.manager -Djava.security.policy==policy.txt//?
-Djava.security.auth.login.config==example.conf MyClient?
What is the weather like today??Sunny?
Authentication succeeded?
The file does not exist in the current working directory?
E:\bench-cluster\cloud-data-preprocess\CoreJavaAdvanced\src>java -Djava.security.manager -Djava.secu rity.policy==com/corejava/chapter9/authorization/policy.txt -Djava.security.auth.login.config==com/c orejava/chapter9/authorization/example.conf com.corejava.chapter9.authorization.MyClient
Attention) A1)? 我的Authorization 實例涉及到的目錄結構
A2)?for complete source code of jaas authentication, please visit ??https://github.com/pacosonTang/core-java-volume/tree/master/coreJavaAdvanced/chapter9/authorization?
結束語?
JAAS是一個能夠認證和強制執行用戶訪問控制的服務的API集合。JAAS認證使用可插入的方式,它不依賴與底層的認證技術;JAAS授權使用以用戶為中心的訪問控制和認證能力提高了以原本以代碼為中心的JAVA安全體系。
總結
以上是生活随笔為你收集整理的java安全 ——JAAS(Java 认证和授权服务)开发指南的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 喜马拉雅怎么一起听书
- 下一篇: myboy是什么意思中文 myboy的意
