java ee jaas_Java Web系列:JAAS认证和授权基础
1.認證和授權概述
(1)認證:對用戶的身份進行驗證。
.NET基于的RBS(參考1)的認證和授權相關的核心是2個接口System.Security.Principal.IPrincipal和System.Security.Principal.IIdentity。我們自己實現認證過程,通過Thread.CurrentPrincipal來設置和讀取認證結果。認證成功后設置認證狀態和標識。
Java內置了的JAAS(參考2),核心是javax.security.auth.Subject類和javax.security.Principal接口。java對認證過程也提供了2個類型,javax.security.auth.login.LoginContext類和javax.security.auth.spi.LoginModule接口。我們自己實現認證過程,但只要實現了LoginModule就可以通過LoginContext使用一致的語法。
(2)授權:對用戶的權限進行驗證,通常使用Role(角色)管理權限。
.NET的支持基于角色的授權。.NET的IPrincipal接口的IsInRole方法是授權的核心。有兩種方式使用:1.使用內置的IPrincipal對象(如GenericIdentity),在認證的同時加載用戶的角色roles。2.自定義IPrincipal實現,實現自己的IsInRole邏輯。ASP.NET中實現的的RolePrincipal就通過將邏輯轉發給System.Web.Security.Roles靜態類。Roles依賴System.Web.Security.RoleProvider接口實現角色的查詢,我們可以通過web.config的相關節點來配置自定義的RoleProvider。
JAAS的IPrincipal接口沒有提供IsInRole方法,我們有2個選擇,要么通過多個IPrincipal表示角色,要么自定義實現IPrincipal添加角色支持。Tomcat容器實現的org.apache.catalina.realm.GenericPrincipal就和.NET中的System.Security.Principal.GenericIdentity十分類似的角色實現。
Java的Subject類和IPrincipal接口與.NET的IPrincipal接口和IIdentity的接口不容易對應。為了便于統一理解.NET和Java的核心類型,我們可以從成員的理解,可以認為Java的Principal類型相當于.NET中的IPrincipa和IIdentity兩個類型的作用。Subject只是作為Principal的聚合根。之前提到的Tomcat容器中的GenericPrincipal就即提供了hasRole和getName成員。Tomcat實現的HttpServletRequest對應的成員就是通過GenericPrincipal實現的。
#
成員
.NET
Java
認證類型
AuthenticationType
System.Security.Principal.IIdentity.AuthenticationType
javax.servlet.http.HttpServletRequest.getAuthType()
標識名稱
Name
System.Security.Principal.IIdentity.Name
java.security.Principal.getName()
角色驗證
IsInRole
System.Security.Principal.IPrincipal.IsInRole
javax.servlet.http.HttpServletRequest.isUserInRole()
2..NET Web認證和授權
ASP.NET Forms認證主采用RolePrincipal主體,未認證用戶設置為GenericIdentity標識,已認證用戶設置為FormsIdentity。RolePrincipal在驗證角色時將使用Roles靜態類通過RoleProvider進行角色驗證。通過HttpContext.User可以直接調用主體。
實現一個最簡單的自定義RoleProvider只需要繼承并實現GetRolesForUser和IsUserInRole兩個方法,通常可以使用委托在Application_Start中注入的方式實現通用的RoleProvider。
ASP.NET的forms驗證通過FormsAuthentication發送和注銷用于認證的token,通過配置web.config可以讓不同的Web服務器以相同的方式對token加密和解密以適應Web服務器負載均衡。不適用cookie承載token時,可以自定義認證邏輯,比如通過url參數方式承載token配合ssl用于app客戶端驗證等。
.NET的認證和授權示意圖:
自定義RoleProvider的示例,省略了不需要實現的部分代碼,GetRolesForUserDelegate和IsUserInRoleDelegate在Application_Start中注入即可徹底實現RoleProvider和應用服務代碼的解耦:
public classSimpleRoleProvider : RoleProvider
{
public static FuncGetRolesForUserDelegate;
public static FuncIsUserInRoleDelegate;
public override string[] GetRolesForUser(stringusername)
{
returnGetRolesForUserDelegate(username);
}
public override bool IsUserInRole(string username, stringroleName)
{
returnIsUserInRoleDelegate(username, roleName);
}
}
Forms身份驗證和RoleProvider的分別定義在web.config配置文件中。ASP.NET的配置文件示例(省略了其他配置):
.NET中還有用于配置Pricipal的兩個方法System.AppDomain.SetThreadPrincipal和System.AppDomain.SetPrincipalPolicy以及控制訪問的兩個類型System.Security.Permissions.PrincipalPermission和System.Security.Permissions.PrincipalPermissionAttribute。
3.JAAS
HttpServletRequest接口定義了6個驗證和授權相關的方法getAuthType()、login()、logout()、getRemoteUser()、isUserInRole()、getUserPrincipal()。類似ASP.NET,Forms身份驗證也在配置文件中進行配置。但由于Java熱衷于定義一堆接口將實現推遲到容器級別,LoginContext依賴的具體的LoginModule的配置也必須在容器中進行配置。因此除了web.xml,還需要配置在容器中配置JAAS的配置文件。JAAS的示意圖:
(1)JAAS 內置的登錄模塊使用:NTLoginModule:
配置:
first{
com.sun.security.auth.module.NTLoginModule Required debug=true;
};
代碼
packagecom.test.jaas1;importjava.security.Principal;importjavax.security.auth.login.LoginContext;importjavax.security.auth.login.LoginException;public classApp {public static voidmain(String[] args) {
System.setProperty("java.security.auth.login.config",
Thread.currentThread().getContextClassLoader().getResource("jaas.config").getPath());try{
LoginContext lc= new LoginContext("first");
lc.login();
System.out.println(lc.getSubject().getPrincipals().size());for(Principal item : lc.getSubject().getPrincipals()) {
System.out.println(String.format("%s principal:%s", item.getClass().getTypeName(), item.getName()));
}
lc.logout();
System.out.println(lc.getSubject().getPrincipals().size());
}catch(LoginException e) {
e.printStackTrace();
}
}
}
(2)JAAS Tomcat容器下的登錄模塊使用(參考3):
用于配置Forms認證:web.xml
Archetype Created Web Application
Admin
/admin/*
admin
admin
FORM
/login.html
/error.html
View Code
用于JAAS的LoginModule的配置:/main/java/resources/jaas.config
MyLogin{
MyLoginModule Required debug=true;
};
View Code
用于Tomcat的配置:/main/webapp/META-INF/context.xml配置(這個依賴至少還在項目內,不需要修改tomcat):
View Code
用于Tomcat的UserClass和RoleClass代碼:
importjava.security.Principal;public class UserPrincipal implementsPrincipal {privateString _name;publicUserPrincipal(String name) {this._name =name;
}
@OverridepublicString getName() {return this._name;
}
}
View Code
RoleClass:
importjava.security.Principal;public class RolePrincipal implementsPrincipal {privateString _name;publicRolePrincipal(String name) {this._name =name;
}
@OverridepublicString getName() {return this._name;
}
}
View Code
用于JAAS配置文件初始化的代碼(為了依賴tomcat的配置,在Filter中設置配置文件):
importjava.io.IOException;importjavax.servlet.Filter;importjavax.servlet.FilterChain;importjavax.servlet.FilterConfig;importjavax.servlet.ServletException;importjavax.servlet.ServletRequest;importjavax.servlet.ServletResponse;importjavax.servlet.annotation.WebFilter;importjavax.servlet.http.HttpServletRequest;
@WebFilter("/*")public class MyFilter implementsFilter {
@Overridepublic void init(FilterConfig filterConfig) throwsServletException {//TODO 自動生成的方法存根
System.setProperty("java.security.auth.login.config",
Thread.currentThread().getContextClassLoader().getResource("jaas.config").getPath());
}
@Overridepublic voiddoFilter(ServletRequest request, ServletResponse response, FilterChain chain)throwsIOException, ServletException {
chain.doFilter(newMyRequest((HttpServletRequest) request), response);
}
@Overridepublic voiddestroy() {//TODO 自動生成的方法存根
}
}
View Code
用于登錄的login.html:
Insert title here| UserName | |
| Password | |
View Code
MyLoginModule實現:其中的三個name屬性必須是固定值:j_security_check、j_username和j_password。
importjava.io.IOException;importjava.util.Map;importjavax.security.auth.Subject;importjavax.security.auth.callback.Callback;importjavax.security.auth.callback.CallbackHandler;importjavax.security.auth.callback.NameCallback;importjavax.security.auth.callback.PasswordCallback;importjavax.security.auth.callback.UnsupportedCallbackException;importjavax.security.auth.login.LoginException;importjavax.security.auth.spi.LoginModule;public class MyLoginModule implementsLoginModule {privateCallbackHandler handler;privateSubject subject;
@Overridepublic void initialize(Subject subject, CallbackHandler callbackHandler, MapsharedState,
Mapoptions) {
handler=callbackHandler;this.subject =subject;
}
@Overridepublic boolean login() throwsLoginException {
Callback[] callbacks= new Callback[2];
callbacks[0] = new NameCallback("login");
callbacks[1] = new PasswordCallback("password", true);try{
handler.handle(callbacks);
String name= ((NameCallback) callbacks[0]).getName();
String password= String.valueOf(((PasswordCallback) callbacks[1]).getPassword());if (name != null && name.equals("user123") && password != null && password.equals("pass123")) {return true;
}//If credentials are NOT OK we throw a LoginException
throw new LoginException("Authentication failed");
}catch(IOException e) {throw newLoginException(e.getMessage());
}catch(UnsupportedCallbackException e) {throw newLoginException(e.getMessage());
}
}
@Overridepublic boolean commit() throwsLoginException {
subject.getPrincipals().add(new UserPrincipal("user123"));
subject.getPrincipals().add(new RolePrincipal("admin"));return true;
}
@Overridepublic boolean abort() throwsLoginException {//TODO 自動生成的方法存根
return false;
}
@Overridepublic boolean logout() throwsLoginException {
subject.getPrincipals().clear();return true;
}
}
View Code
在.NET的RBS基礎上實現RBAC(參考4)是可行的,但JAAS....。JAAS只要用過的人都對其印象深刻。
4.參考
(1)https://msdn.microsoft.com/en-us/library/52kd59t0(v=vs.90).aspx
(2)http://docs.oracle.com/javase/8/docs/technotes/guides/security/jaas/JAASRefGuide.html
(3)http://www.byteslounge.com/tutorials/jaas-form-based-authentication-in-tomcat-example
(4)http://csrc.nist.gov/groups/SNS/rbac/
5.小結:
JAAS抽象的不切實際,實現又全靠容器,不同容器的實現還不一致,IPrincipal又不能直接支持Servlet認證和授權相關的方法。至少應該像.NET一樣提供數據結構級別的角色認證類型,而不是跑偏成現在這樣。容器要么自己擴展IPrincipal支持角色,要么通過配置傳入指定類型的IPrincipal子類來區分角色和用戶。只能希望Apache Shiro和Spring等第三方提供的一些實現能有更高的可用性。
總結
以上是生活随笔為你收集整理的java ee jaas_Java Web系列:JAAS认证和授权基础的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 主控芯片
- 下一篇: 接口测试学习——操作MySQL