Apache Shiro 使用手冊

一、在web.xml中添加shiro過濾器 
Xml代碼  收藏代碼
  1. <!-- Shiro filter-->  
  2. <filter>  
  3.     <filter-name>shiroFilter</filter-name>  
  4.     <filter-class>  
  5.         org.springframework.web.filter.DelegatingFilterProxy  
  6.     </filter-class>  
  7. </filter>  
  8. <filter-mapping>  
  9.     <filter-name>shiroFilter</filter-name>  
  10.     <url-pattern>/*</url-pattern>  
  11. </filter-mapping>  

二、在Spring的applicationContext.xml中添加shiro配置 
1、添加shiroFilter定義 
Xml代碼  收藏代碼
  1. <!-- Shiro Filter -->  
  2. <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
  3.     <property name="securityManager" ref="securityManager" />  
  4.     <property name="loginUrl" value="/login" />  
  5.     <property name="successUrl" value="/user/list" />  
  6.     <property name="unauthorizedUrl" value="/login" />  
  7.     <property name="filterChainDefinitions">  
  8.         <value>  
  9.             /login = anon  
  10.             /user/** = authc  
  11.             /role/edit/* = perms[role:edit]  
  12.             /role/save = perms[role:edit]  
  13.             /role/list = perms[role:view]  
  14.             /** = authc  
  15.         </value>  
  16.     </property>  
  17. </bean>  

2、添加securityManager定義 
Xml代碼  收藏代碼
  1. <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
  2.     <property name="realm" ref="myRealm" />  
  3. </bean>  

3、添加realm定義 
Xml代碼  收藏代碼
  1. <bean id=" myRealm" class="com...MyRealm" />  

三、實現MyRealm:繼承AuthorizingRealm,並重寫認證授權方法 
Java代碼  收藏代碼
  1. public class MyRealm extends AuthorizingRealm{  
  2.   
  3.     private AccountManager accountManager;  
  4.     public void setAccountManager(AccountManager accountManager) {  
  5.         this.accountManager = accountManager;  
  6.     }  
  7.   
  8.     /** 
  9.      * 授權信息 
  10.      */  
  11.     protected AuthorizationInfo doGetAuthorizationInfo(  
  12.                 PrincipalCollection principals) {  
  13.         String username=(String)principals.fromRealm(getName()).iterator().next();  
  14.         if( username != null ){  
  15.             User user = accountManager.get( username );  
  16.             if( user != null && user.getRoles() != null ){  
  17.                 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();  
  18.                 for( SecurityRole each: user.getRoles() ){  
  19.                         info.addRole(each.getName());  
  20.                         info.addStringPermissions(each.getPermissionsAsString());  
  21.                 }  
  22.                 return info;  
  23.             }  
  24.         }  
  25.         return null;  
  26.     }  
  27.   
  28.     /** 
  29.      * 認證信息 
  30.      */  
  31.     protected AuthenticationInfo doGetAuthenticationInfo(  
  32.                 AuthenticationToken authcToken ) throws AuthenticationException {  
  33.         UsernamePasswordToken token = (UsernamePasswordToken) authcToken;  
  34.         String userName = token.getUsername();  
  35.         if( userName != null && !"".equals(userName) ){  
  36.             User user = accountManager.login(token.getUsername(),  
  37.                             String.valueOf(token.getPassword()));  
  38.   
  39.             if( user != null )  
  40.                 return new SimpleAuthenticationInfo(  
  41.                             user.getLoginName(),user.getPassword(), getName());  
  42.         }  
  43.         return null;  
  44.     }  
  45.   
  46. }  


Apache Shiro 使用手冊(一)Shiro架構介紹

一、什麼是Shiro 

Apache Shiro是一個強大易用的Java安全框架,提供了認證、授權、加密和會話管理等功能: 
  • 認證 - 用戶身份識別,常被稱爲用戶“登錄”;
  • 授權 - 訪問控制;
  • 密碼加密 - 保護或隱藏數據防止被偷窺;
  • 會話管理 - 每用戶相關的時間敏感的狀態。
對於任何一個應用程序,Shiro都可以提供全面的安全管理服務。並且相對於其他安全框架,Shiro要簡單的多。 

二、Shiro的架構介紹 
首先,來了解一下Shiro的三個核心組件:Subject, SecurityManager 和 Realms. 如下圖: 
 
Subject:即“當前操作用戶”。但是,在Shiro中,Subject這一概念並不僅僅指人,也可以是第三方進程、後臺帳戶(Daemon Account)或其他類似事物。它僅僅意味着“當前跟軟件交互的東西”。但考慮到大多數目的和用途,你可以把它認爲是Shiro的“用戶”概念。 
Subject代表了當前用戶的安全操作,SecurityManager則管理所有用戶的安全操作。 

SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通過SecurityManager來管理內部組件實例,並通過它來提供安全管理的各種服務。 

Realm: Realm充當了Shiro與應用安全數據間的“橋樑”或者“連接器”。也就是說,當對用戶執行認證(登錄)和授權(訪問控制)驗證時,Shiro會從應用配置的Realm中查找用戶及其權限信息。 
從這個意義上講,Realm實質上是一個安全相關的DAO:它封裝了數據源的連接細節,並在需要時將相關數據提供給Shiro。當配置Shiro時,你必須至少指定一個Realm,用於認證和(或)授權。配置多個Realm是可以的,但是至少需要一個。 
Shiro內置了可以連接大量安全數據源(又名目錄)的Realm,如LDAP、關係數據庫(JDBC)、類似INI的文本配置資源以及屬性文件等。如果缺省的Realm不能滿足需求,你還可以插入代表自定義數據源的自己的Realm實現。 


Shiro完整架構圖: 

 
除前文所講Subject、SecurityManager 、Realm三個核心組件外,Shiro主要組件還包括: 
Authenticator :認證就是覈實用戶身份的過程。這個過程的常見例子是大家都熟悉的“用戶/密碼”組合。多數用戶在登錄軟件系統時,通常提供自己的用戶名(當事人)和支持他們的密碼(證書)。如果存儲在系統中的密碼(或密碼錶示)與用戶提供的匹配,他們就被認爲通過認證。 
Authorizer :授權實質上就是訪問控制 - 控制用戶能夠訪問應用中的哪些內容,比如資源、Web頁面等等。 
SessionManager :在安全框架領域,Apache Shiro提供了一些獨特的東西:可在任何應用或架構層一致地使用Session API。即,Shiro爲任何應用提供了一個會話編程範式 - 從小型後臺獨立應用到大型集羣Web應用。這意味着,那些希望使用會話的應用開發者,不必被迫使用Servlet或EJB容器了。或者,如果正在使用這些容器,開發者現在也可以選擇使用在任何層統一一致的會話API,取代Servlet或EJB機制。 

CacheManager :對Shiro的其他組件提供緩存支持。 


認證就是驗證用戶身份的過程。在認證過程中,用戶需要提交實體信息(Principals)和憑據信息(Credentials)以檢驗用戶是否合法。最常見的“實體/憑證”組合便是“用戶名/密碼”組合。 

一、Shiro認證過程 

1、收集實體/憑據信息 
Java代碼  收藏代碼
  1. //Example using most common scenario of username/password pair:  
  2. UsernamePasswordToken token = new UsernamePasswordToken(username, password);  
  3. //”Remember Me” built-in:  
  4. token.setRememberMe(true);  
UsernamePasswordToken支持最常見的用戶名/密碼的認證機制。同時,由於它實現了RememberMeAuthenticationToken接口,我們可以通過令牌設置“記住我”的功能。 
但是,“已記住”和“已認證”是有區別的: 
已記住的用戶僅僅是非匿名用戶,你可以通過subject.getPrincipals()獲取用戶信息。但是它並非是完全認證通過的用戶,當你訪問需要認證用戶的功能時,你仍然需要重新提交認證信息。 
這一區別可以參考亞馬遜網站,網站會默認記住登錄的用戶,再次訪問網站時,對於非敏感的頁面功能,頁面上會顯示記住的用戶信息,但是當你訪問網站賬戶信息時仍然需要再次進行登錄認證。 

2、提交實體/憑據信息 
Java代碼  收藏代碼
  1. Subject currentUser = SecurityUtils.getSubject();  
  2. currentUser.login(token);  
收集了實體/憑據信息之後,我們可以通過SecurityUtils工具類,獲取當前的用戶,然後通過調用login方法提交認證。 

3、認證處理 
Java代碼  收藏代碼
  1. try {  
  2.     currentUser.login(token);  
  3. catch ( UnknownAccountException uae ) { ...  
  4. catch ( IncorrectCredentialsException ice ) { ...  
  5. catch ( LockedAccountException lae ) { ...  
  6. catch ( ExcessiveAttemptsException eae ) { ...  
  7. } ... catch your own ...  
  8. catch ( AuthenticationException ae ) {  
  9.     //unexpected error?  
  10. }  
如果login方法執行完畢且沒有拋出任何異常信息,那麼便認爲用戶認證通過。之後在應用程序任意地方調用SecurityUtils.getSubject() 都可以獲取到當前認證通過的用戶實例,使用subject.isAuthenticated()判斷用戶是否已驗證都將返回true. 
相反,如果login方法執行過程中拋出異常,那麼將認爲認證失敗。Shiro有着豐富的層次鮮明的異常類來描述認證失敗的原因,如代碼示例。 

二、登出操作 
登出操作可以通過調用subject.logout()來刪除你的登錄信息,如: 
Java代碼  收藏代碼
  1. currentUser.logout(); //removes all identifying information and invalidates their session too.  
當執行完登出操作後,Session信息將被清空,subject將被視作爲匿名用戶。 

三、認證內部處理機制 
以上,是Shiro認證在應用程序中的處理過程,下面將詳細解說Shiro認證的內部處理機制。 
 

如上圖,我們通過Shiro架構圖的認證部分,來說明Shiro認證內部的處理順序: 
1、應用程序構建了一個終端用戶認證信息的AuthenticationToken 實例後,調用Subject.login方法。 
2、Sbuject的實例通常是DelegatingSubject類(或子類)的實例對象,在認證開始時,會委託應用程序設置的securityManager實例調用securityManager.login(token)方法。 
3、SecurityManager接受到token(令牌)信息後會委託內置的Authenticator的實例(通常都是ModularRealmAuthenticator類的實例)調用authenticator.authenticate(token). ModularRealmAuthenticator在認證過程中會對設置的一個或多個Realm實例進行適配,它實際上爲Shiro提供了一個可拔插的認證機制。 
4、如果在應用程序中配置了多個Realm,ModularRealmAuthenticator會根據配置的AuthenticationStrategy(認證策略)來進行多Realm的認證過程。在Realm被調用後,AuthenticationStrategy將對每一個Realm的結果作出響應。 
注:如果應用程序中僅配置了一個Realm,Realm將被直接調用而無需再配置認證策略。 
5、判斷每一個Realm是否支持提交的token,如果支持,Realm將調用getAuthenticationInfo(token); getAuthenticationInfo 方法就是實際認證處理,我們通過覆蓋Realm的doGetAuthenticationInfo方法來編寫我們自定義的認證處理。 

四、使用多個Realm的處理機制: 

1、Authenticator 
默認實現是ModularRealmAuthenticator,它既支持單一Realm也支持多個Realm。如果僅配置了一個Realm,ModularRealmAuthenticator 會直接調用該Realm處理認證信息,如果配置了多個Realm,它會根據認證策略來適配Realm,找到合適的Realm執行認證信息。 
自定義Authenticator的配置: 
Java代碼  收藏代碼
  1. [main]  
  2. ...  
  3. authenticator = com.foo.bar.CustomAuthenticator  
  4. securityManager.authenticator = $authenticator  

2、AuthenticationStrategy(認證策略) 
當應用程序配置了多個Realm時,ModularRealmAuthenticator將根據認證策略來判斷認證成功或是失敗。 
例如,如果只有一個Realm驗證成功,而其他Realm驗證失敗,那麼這次認證是否成功呢?如果大多數的Realm驗證成功了,認證是否就認爲成功呢?或者,一個Realm驗證成功後,是否還需要判斷其他Realm的結果?認證策略就是根據應用程序的需要對這些問題作出決斷。 
認證策略是一個無狀態的組件,在認證過程中會經過4次的調用: 
  • 在所有Realm被調用之前
  • 在調用Realm的getAuthenticationInfo 方法之前
  • 在調用Realm的getAuthenticationInfo 方法之後
  • 在所有Realm被調用之後
認證策略的另外一項工作就是聚合所有Realm的結果信息封裝至一個AuthenticationInfo實例中,並將此信息返回,以此作爲Subject的身份信息。 
Shiro有3中認證策略的具體實現: 
AtLeastOneSuccessfulStrategy 只要有一個(或更多)的Realm驗證成功,那麼認證將被視爲成功
FirstSuccessfulStrategy 第一個Realm驗證成功,整體認證將被視爲成功,且後續Realm將被忽略
AllSuccessfulStrategy 所有Realm成功,認證才視爲成功

ModularRealmAuthenticator 內置的認證策略默認實現是AtLeastOneSuccessfulStrategy 方式,因爲這種方式也是被廣泛使用的一種認證策略。當然,你也可以通過配置文件定義你需要的策略,如: 
Java代碼  收藏代碼
  1. [main]  
  2. ...  
  3. authcStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy  
  4. securityManager.authenticator.authenticationStrategy = $authcStrategy  
  5. ...  

3、Realm的順序 
由剛纔提到的認證策略,可以看到Realm在ModularRealmAuthenticator 裏面的順序對認證是有影響的。 
ModularRealmAuthenticator 會讀取配置在SecurityManager裏的Realm。當執行認證是,它會遍歷Realm集合,對所有支持提交的token的Realm調用getAuthenticationInfo 。 
因此,如果Realm的順序對你使用的認證策略結果有影響,那麼你應該在配置文件中明確定義Realm的順序,如: 
Java代碼  收藏代碼
  1. blahRealm = com.company.blah.Realm  
  2. ...  
  3. fooRealm = com.company.foo.Realm  
  4. ...  
  5. barRealm = com.company.another.Realm  
  6.   
  7. securityManager.realms = $fooRealm, $barRealm, $blahRealm  

授權即訪問控制,它將判斷用戶在應用程序中對資源是否擁有相應的訪問權限。 
如,判斷一個用戶有查看頁面的權限,編輯數據的權限,擁有某一按鈕的權限,以及是否擁有打印的權限等等。 

一、授權的三要素 

授權有着三個核心元素:權限、角色和用戶。 

權限 
權限是Apache Shiro安全機制最核心的元素。它在應用程序中明確聲明瞭被允許的行爲和表現。一個格式良好好的權限聲明可以清晰表達出用戶對該資源擁有的權限。 
大多數的資源會支持典型的CRUD操作(create,read,update,delete),但是任何操作建立在特定的資源上纔是有意義的。因此,權限聲明的根本思想就是建立在資源以及操作上。 
而我們通過權限聲明僅僅能瞭解這個權限可以在應用程序中做些什麼,而不能確定誰擁有此權限。 
於是,我們就需要在應用程序中對用戶和權限建立關聯。 
通常的做法就是將權限分配給某個角色,然後將這個角色關聯一個或多個用戶。 

權限聲明及粒度 
Shiro權限聲明通常是使用以冒號分隔的表達式。就像前文所講,一個權限表達式可以清晰的指定資源類型,允許的操作,可訪問的數據。同時,Shiro權限表達式支持簡單的通配符,可以更加靈活的進行權限設置。 
下面以實例來說明權限表達式。 
可查詢用戶數據 
User:view 
可查詢或編輯用戶數據 
User:view,edit 
可對用戶數據進行所有操作 
User:* 或 user 
可編輯id爲123的用戶數據 
User:edit:123 

角色 
Shiro支持兩種角色模式: 
1、傳統角色:一個角色代表着一系列的操作,當需要對某一操作進行授權驗證時,只需判斷是否是該角色即可。這種角色權限相對簡單、模糊,不利於擴展。 
2、權限角色:一個角色擁有一個權限的集合。授權驗證時,需要判斷當前角色是否擁有該權限。這種角色權限可以對該角色進行詳細的權限描述,適合更復雜的權限設計。 
下面將詳細描述對兩種角色模式的授權實現。 

二、授權實現 

Shiro支持三種方式實現授權過程: 
  • 編碼實現
  • 註解實現
  • JSP Taglig實現
1、基於編碼的授權實現 

1.1基於傳統角色授權實現
 
當需要驗證用戶是否擁有某個角色時,可以調用Subject 實例的hasRole*方法驗證。 
Java代碼  收藏代碼
  1. Subject currentUser = SecurityUtils.getSubject();  
  2. if (currentUser.hasRole("administrator")) {  
  3.     //show the admin button  
  4. else {  
  5.     //don't show the button?  Grey it out?  
  6. }  

相關驗證方法如下: 
Subject方法 描述
hasRole(String roleName) 當用戶擁有指定角色時,返回true
hasRoles(List<String> roleNames) 按照列表順序返回相應的一個boolean值數組
hasAllRoles(Collection<String> roleNames) 如果用戶擁有所有指定角色時,返回true

斷言支持 
Shiro還支持以斷言的方式進行授權驗證。斷言成功,不返回任何值,程序繼續執行;斷言失敗時,將拋出異常信息。使用斷言,可以使我們的代碼更加簡潔。 
Java代碼  收藏代碼
  1. Subject currentUser = SecurityUtils.getSubject();  
  2. //guarantee that the current user is a bank teller and  
  3. //therefore allowed to open the account:  
  4. currentUser.checkRole("bankTeller");  
  5. openBankAccount();  

斷言的相關方法: 
Subject方法 描述
checkRole(String roleName) 斷言用戶是否擁有指定角色
checkRoles(Collection<String> roleNames) 斷言用戶是否擁有所有指定角色
checkRoles(String... roleNames) 對上一方法的方法重載

1.2 基於權限角色授權實現 
相比傳統角色模式,基於權限的角色模式耦合性要更低些,它不會因角色的改變而對源代碼進行修改,因此,基於權限的角色模式是更好的訪問控制方式。 
它的代碼實現有以下幾種實現方式: 
1、基於權限對象的實現 
創建org.apache.shiro.authz.Permission的實例,將該實例對象作爲參數傳遞給Subject.isPermitted()進行驗證。 
Java代碼  收藏代碼
  1. Permission printPermission = new PrinterPermission("laserjet4400n""print");  
  2. Subject currentUser = SecurityUtils.getSubject();  
  3. if (currentUser.isPermitted(printPermission)) {  
  4.     //show the Print button  
  5. else {  
  6.     //don't show the button?  Grey it out?  
  7. }  
  8. Permission printPermission = new PrinterPermission("laserjet4400n""print");  
  9. Subject currentUser = SecurityUtils.getSubject();  
  10. if (currentUser.isPermitted(printPermission)) {  
  11.     //show the Print button  
  12. else {  
  13.     //don't show the button?  Grey it out?  
  14. }  

相關方法如下: 
Subject方法 描述
isPermitted(Permission p) Subject擁有制定權限時,返回treu
isPermitted(List<Permission> perms) 返回對應權限的boolean數組
isPermittedAll(Collection<Permission> perms) Subject擁有所有制定權限時,返回true

2、 基於字符串的實現 
相比笨重的基於對象的實現方式,基於字符串的實現便顯得更加簡潔。 
Java代碼  收藏代碼
  1. Subject currentUser = SecurityUtils.getSubject();  
  2. if (currentUser.isPermitted("printer:print:laserjet4400n")) {  
  3.     //show the Print button  
  4. else {  
  5.     //don't show the button?  Grey it out?  
  6. }  

使用冒號分隔的權限表達式是org.apache.shiro.authz.permission.WildcardPermission 默認支持的實現方式。 
這裏分別代表了 資源類型:操作:資源ID 

類似基於對象的實現相關方法,基於字符串的實現相關方法: 
isPermitted(String perm)、isPermitted(String... perms)、isPermittedAll(String... perms) 

基於權限對象的斷言實現 
Java代碼  收藏代碼
  1. Subject currentUser = SecurityUtils.getSubject();  
  2. //guarantee that the current user is permitted  
  3. //to open a bank account:  
  4. Permission p = new AccountPermission("open");  
  5. currentUser.checkPermission(p);  
  6. openBankAccount();  

基於字符串的斷言實現 
Java代碼  收藏代碼
  1. Subject currentUser = SecurityUtils.getSubject();  
  2. //guarantee that the current user is permitted  
  3. //to open a bank account:  
  4. currentUser.checkPermission("account:open");  
  5. openBankAccount();  

斷言實現的相關方法 
Subject方法 說明
checkPermission(Permission p) 斷言用戶是否擁有制定權限
checkPermission(String perm) 斷言用戶是否擁有制定權限
checkPermissions(Collection<Permission> perms) 斷言用戶是否擁有所有指定權限
checkPermissions(String... perms) 斷言用戶是否擁有所有指定權限

2、基於註解的授權實現 
Shiro註解支持AspectJ、Spring、Google-Guice等,可根據應用進行不同的配置。 

相關的註解: 
@ RequiresAuthentication 
可以用戶類/屬性/方法,用於表明當前用戶需是經過認證的用戶。 
Java代碼  收藏代碼
  1. @RequiresAuthentication  
  2. public void updateAccount(Account userAccount) {  
  3.     //this method will only be invoked by a   
  4.     //Subject that is guaranteed authenticated  
  5.     ...  
  6. }  

@ RequiresGuest 
表明該用戶需爲”guest”用戶 

@ RequiresPermissions 
當前用戶需擁有制定權限 
Java代碼  收藏代碼
  1. @RequiresPermissions("account:create")  
  2. public void createAccount(Account account) {  
  3.     //this method will only be invoked by a Subject  
  4.     //that is permitted to create an account  
  5.     ...  
  6. }  

@RequiresRoles 
當前用戶需擁有制定角色 

@ RequiresUser 
當前用戶需爲已認證用戶或已記住用戶 

3、基於JSP  TAG的授權實現 
Shiro提供了一套JSP標籤庫來實現頁面級的授權控制。 
在使用Shiro標籤庫前,首先需要在JSP引入shiro標籤: 
Java代碼  收藏代碼
  1. <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>  

下面一一介紹Shiro的標籤: 
guest標籤 
驗證當前用戶是否爲“訪客”,即未認證(包含未記住)的用戶 
Xml代碼  收藏代碼
  1. <shiro:guest>  
  2.     Hi there!  Please <a href="login.jsp">Login</a> or <a href="signup.jsp">Signup</a> today!  
  3. </shiro:guest>  

user標籤 
認證通過或已記住的用戶 
Xml代碼  收藏代碼
  1. <shiro:user>  
  2.     Welcome back John!  Not John? Click <a href="login.jsp">here<a> to login.  
  3. </shiro:user>  

authenticated標籤 
已認證通過的用戶。不包含已記住的用戶,這是與user標籤的區別所在。 
Xml代碼  收藏代碼
  1. <shiro:authenticated>  
  2.     <a href="updateAccount.jsp">Update your contact information</a>.  
  3. </shiro:authenticated>  

notAuthenticated標籤 
未認證通過用戶,與authenticated標籤相對應。與guest標籤的區別是,該標籤包含已記住用戶。 
Xml代碼  收藏代碼
  1. <shiro:notAuthenticated>  
  2.     Please <a href="login.jsp">login</a> in order to update your credit card information.  
  3. </shiro:notAuthenticated>  

principal 標籤 
輸出當前用戶信息,通常爲登錄帳號信息 
Xml代碼  收藏代碼
  1. Hello, <shiro:principal/>, how are you today?  

hasRole標籤 
驗證當前用戶是否屬於該角色 
Xml代碼  收藏代碼
  1. <shiro:hasRole name="administrator">  
  2.     <a href="admin.jsp">Administer the system</a>  
  3. </shiro:hasRole>  

lacksRole標籤 
與hasRole標籤邏輯相反,當用戶不屬於該角色時驗證通過 
Xml代碼  收藏代碼
  1. <shiro:lacksRole name="administrator">  
  2.     Sorry, you are not allowed to administer the system.  
  3. </shiro:lacksRole>  

hasAnyRole標籤 
驗證當前用戶是否屬於以下任意一個角色。 
Xml代碼  收藏代碼
  1. <shiro:hasAnyRoles name="developer, project manager, administrator">  
  2.     You are either a developer, project manager, or administrator.  
  3. </shiro:lacksRole>  

hasPermission標籤 
驗證當前用戶是否擁有制定權限 
Xml代碼  收藏代碼
  1. <shiro:hasPermission name="user:create">  
  2.     <a href="createUser.jsp">Create a new User</a>  
  3. </shiro:hasPermission>  

lacksPermission標籤 
與hasPermission標籤邏輯相反,當前用戶沒有制定權限時,驗證通過 
Xml代碼  收藏代碼
  1. <shiro:hasPermission name="user:create">  
  2.     <a href="createUser.jsp">Create a new User</a>  
  3. </shiro:hasPermission>  


三、Shiro授權的內部處理機制 
 
1、在應用程序中調用授權驗證方法(Subject的isPermitted*或hasRole*等) 
2、Sbuject的實例通常是DelegatingSubject類(或子類)的實例對象,在認證開始時,會委託應用程序設置的securityManager實例調用相應的isPermitted*或hasRole*方法。 
3、接下來SecurityManager會委託內置的Authorizer的實例(默認是ModularRealmAuthorizer 類的實例,類似認證實例,它同樣支持一個或多個Realm實例認證)調用相應的授權方法。 
4、每一個Realm將檢查是否實現了相同的 Authorizer 接口。然後,將調用Reaml自己的相應的授權驗證方法。 

當使用多個Realm時,不同於認證策略處理方式,授權處理過程中: 
1、當調用Realm出現異常時,將立即拋出異常,結束授權驗證。 
2、只要有一個Realm驗證成功,那麼將認爲授權成功,立即返回,結束認證。 

在認證、授權內部實現機制中都有提到,最終處理都將交給Real進行處理。因爲在Shiro中,最終是通過Realm來獲取應用程序中的用戶、角色及權限信息的。通常情況下,在Realm中會直接從我們的數據源中獲取Shiro需要的驗證信息。可以說,Realm是專用於安全框架的DAO. 

一、認證實現 
正如前文所提到的,Shiro的認證過程最終會交由Realm執行,這時會調用Realm的getAuthenticationInfo(token)方法。 
該方法主要執行以下操作: 
1、檢查提交的進行認證的令牌信息 
2、根據令牌信息從數據源(通常爲數據庫)中獲取用戶信息 
3、對用戶信息進行匹配驗證。 
4、驗證通過將返回一個封裝了用戶信息的AuthenticationInfo實例。 
5、驗證失敗則拋出AuthenticationException異常信息。 

而在我們的應用程序中要做的就是自定義一個Realm類,繼承AuthorizingRealm抽象類,重載doGetAuthenticationInfo (),重寫獲取用戶信息的方法。 
Java代碼  收藏代碼
  1. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {  
  2.         UsernamePasswordToken token = (UsernamePasswordToken) authcToken;  
  3.         User user = accountManager.findUserByUserName(token.getUsername());  
  4.         if (user != null) {  
  5.             return new SimpleAuthenticationInfo(user.getUserName(), user.getPassword(), getName());  
  6.         } else {  
  7.             return null;  
  8.         }  
  9. }  

二、授權實現 
而授權實現則與認證實現非常相似,在我們自定義的Realm中,重載doGetAuthorizationInfo()方法,重寫獲取用戶權限的方法即可。 
Java代碼  收藏代碼
  1. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {  
  2.         String userName = (String) principals.fromRealm(getName()).iterator().next();  
  3.         User user = accountManager.findUserByUserName(userName);  
  4.         if (user != null) {  
  5.             SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();  
  6.             for (Group group : user.getGroupList()) {  
  7.                 info.addStringPermissions(group.getPermissionList());  
  8.             }  
  9.             return info;  
  10.         } else {  
  11.             return null;  
  12.         }  
  13. }  

Apache Shiro的配置主要分爲四部分: 
  • 對象和屬性的定義與配置
  • URL的過濾器配置
  • 靜態用戶配置
  • 靜態角色配置
其中,由於用戶、角色一般由後臺進行操作的動態數據,因此Shiro配置一般僅包含前兩項的配置。 

Apache Shiro的大多數組件是基於POJO的,因此我們可以使用POJO兼容的任何配置機制進行配置,例如:Java代碼、Sping XML、YAML、JSON、ini文件等等。下面,以Spring XML的配置方式爲例,並且對其中的一些配置參數進行一些簡單說明。 

Shiro對象的配置: 
主要是對Shiro各個組件的實現進行定義配置,主要組件在前文已做過簡單介紹,這裏不再一一說明。 
Xml代碼  收藏代碼
  1. <bean id="securityManager" class="org.apache.shiro.mgt.DefaultSecurityManager">  
  2.         <property name="cacheManager" ref="cacheManager"/>  
  3.         <property name="sessionMode" value="native"/>  
  4.         <!-- Single realm app.  If you have multiple realms, use the 'realms' property instead. -->  
  5.         <property name="realm" ref="myRealm"/>  
  6.         <property name="sessionManager" ref="sessionManager"/>   
  7. </bean>  


Shiro過濾器的配置 
Shiro主要是通過URL過濾來進行安全管理,這裏的配置便是指定具體授權規則定義。 
Xml代碼  收藏代碼
  1. <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
  2.     <property name="securityManager" ref="securityManager"/>  
  3.     <property name="loginUrl" value="/login.jsp"/>  
  4.     <property name="successUrl" value="/home.jsp"/>  
  5.     <property name="unauthorizedUrl" value="/unauthorized.jsp"/> -->  
  6.     <property name="filterChainDefinitions">  
  7.         <value>  
  8.             # some example chain definitions:  
  9.             /admin/** = authc, roles[admin]  
  10.             /docs/** = authc, perms[document:read]  
  11.             /** = authc  
  12.             # more URL-to-FilterChain definitions here  
  13.         </value>  
  14.     </property>  
  15. </bean>  

URL過濾器配置說明: 
Shiro可以通過配置文件實現基於URL的授權驗證。FilterChain定義格式: 
URL_Ant_Path_Expression = Path_Specific_Filter_Chain 
每個URL配置,表示匹配該URL的應用程序請求將由對應的過濾器進行驗證。 
例如: 
[urls] 
/index.html = anon 
/user/create = anon 
/user/** = authc 
/admin/** = authc, roles[administrator] 
/rest/** = authc, rest 
/remoting/rpc/** = authc, perms["remote:invoke"] 

URL表達式說明 
1、URL目錄是基於HttpServletRequest.getContextPath()此目錄設置 
2、URL可使用通配符,**代表任意子目錄 
3、Shiro驗證URL時,URL匹配成功便不再繼續匹配查找。所以要注意配置文件中的URL順序,尤其在使用通配符時。 

Filter Chain定義說明 
1、一個URL可以配置多個Filter,使用逗號分隔 
2、當設置多個過濾器時,全部驗證通過,才視爲通過 
3、部分過濾器可指定參數,如perms,roles 

Shiro內置的FilterChain 
Filter Name Class
anon org.apache.shiro.web.filter.authc.AnonymousFilter
authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
port org.apache.shiro.web.filter.authz.PortFilter
rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
ssl org.apache.shiro.web.filter.authz.SslFilter
user org.apache.shiro.web.filter.authc.UserFilter














































發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章