Apache Shiro 使用手冊(三)Shiro 授權(轉)
授權即訪問控制,它將判斷用戶在應用程序中對資源是否擁有相應的訪問權限。
如,判斷一個用戶有查看頁面的權限,編輯數據的權限,擁有某一按鈕的權限,以及是否擁有打印的權限等等。
一、授權的三要素
授權有著三個核心元素:權限、角色和用戶。
權限
權限是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支持三種方式實現授權過程:
1.1基于傳統角色授權實現
當需要驗證用戶是否擁有某個角色時,可以調用Subject 實例的hasRole*方法驗證。
相關驗證方法如下:
斷言支持
Shiro還支持以斷言的方式進行授權驗證。斷言成功,不返回任何值,程序繼續執行;斷言失敗時,將拋出異常信息。使用斷言,可以使我們的代碼更加簡潔。
斷言的相關方法:
1.2 基于權限角色授權實現
相比傳統角色模式,基于權限的角色模式耦合性要更低些,它不會因角色的改變而對源代碼進行修改,因此,基于權限的角色模式是更好的訪問控制方式。
它的代碼實現有以下幾種實現方式:
1、基于權限對象的實現
創建org.apache.shiro.authz.Permission的實例,將該實例對象作為參數傳遞給Subject.isPermitted()進行驗證。
相關方法如下:
2、 基于字符串的實現
相比笨重的基于對象的實現方式,基于字符串的實現便顯得更加簡潔。
使用冒號分隔的權限表達式是org.apache.shiro.authz.permission.WildcardPermission 默認支持的實現方式。
這里分別代表了 資源類型:操作:資源ID
類似基于對象的實現相關方法,基于字符串的實現相關方法:
isPermitted(String perm)、isPermitted(String... perms)、isPermittedAll(String... perms)
基于權限對象的斷言實現
基于字符串的斷言實現
斷言實現的相關方法
2、基于注解的授權實現
Shiro注解支持AspectJ、Spring、Google-Guice等,可根據應用進行不同的配置。
相關的注解:
@ RequiresAuthentication
可以用戶類/屬性/方法,用于表明當前用戶需是經過認證的用戶。
@ RequiresGuest
表明該用戶需為”guest”用戶
@ RequiresPermissions
當前用戶需擁有制定權限
@RequiresRoles
當前用戶需擁有制定角色
@ RequiresUser
當前用戶需為已認證用戶或已記住用戶
3、基于JSP TAG的授權實現
Shiro提供了一套JSP標簽庫來實現頁面級的授權控制。
在使用Shiro標簽庫前,首先需要在JSP引入shiro標簽:
下面一一介紹Shiro的標簽:
guest標簽
驗證當前用戶是否為“訪客”,即未認證(包含未記住)的用戶
user標簽
認證通過或已記住的用戶
authenticated標簽
已認證通過的用戶。不包含已記住的用戶,這是與user標簽的區別所在。
notAuthenticated標簽
未認證通過用戶,與authenticated標簽相對應。與guest標簽的區別是,該標簽包含已記住用戶。
principal 標簽
輸出當前用戶信息,通常為登錄帳號信息
hasRole標簽
驗證當前用戶是否屬于該角色
lacksRole標簽
與hasRole標簽邏輯相反,當用戶不屬于該角色時驗證通過
hasAnyRole標簽
驗證當前用戶是否屬于以下任意一個角色。
hasPermission標簽
驗證當前用戶是否擁有制定權限
lacksPermission標簽
與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驗證成功,那么將認為授權成功,立即返回,結束認證。
轉自http://kdboy.iteye.com/blog/1155450
如,判斷一個用戶有查看頁面的權限,編輯數據的權限,擁有某一按鈕的權限,以及是否擁有打印的權限等等。
一、授權的三要素
授權有著三個核心元素:權限、角色和用戶。
權限
權限是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基于傳統角色授權實現
當需要驗證用戶是否擁有某個角色時,可以調用Subject 實例的hasRole*方法驗證。
Java代碼
- Subject currentUser = SecurityUtils.getSubject();
- if (currentUser.hasRole("administrator")) {
- //show the admin button
- } else {
- //don't show the button? Grey it out?
- }
相關驗證方法如下:
Subject方法 | 描述 |
hasRole(String roleName) | 當用戶擁有指定角色時,返回true |
hasRoles(List<String> roleNames) | 按照列表順序返回相應的一個boolean值數組 |
hasAllRoles(Collection<String> roleNames) | 如果用戶擁有所有指定角色時,返回true |
斷言支持
Shiro還支持以斷言的方式進行授權驗證。斷言成功,不返回任何值,程序繼續執行;斷言失敗時,將拋出異常信息。使用斷言,可以使我們的代碼更加簡潔。
Java代碼
- Subject currentUser = SecurityUtils.getSubject();
- //guarantee that the current user is a bank teller and
- //therefore allowed to open the account:
- currentUser.checkRole("bankTeller");
- openBankAccount();
斷言的相關方法:
Subject方法 | 描述 |
checkRole(String roleName) | 斷言用戶是否擁有指定角色 |
checkRoles(Collection<String> roleNames) | 斷言用戶是否擁有所有指定角色 |
checkRoles(String... roleNames) | 對上一方法的方法重載 |
1.2 基于權限角色授權實現
相比傳統角色模式,基于權限的角色模式耦合性要更低些,它不會因角色的改變而對源代碼進行修改,因此,基于權限的角色模式是更好的訪問控制方式。
它的代碼實現有以下幾種實現方式:
1、基于權限對象的實現
創建org.apache.shiro.authz.Permission的實例,將該實例對象作為參數傳遞給Subject.isPermitted()進行驗證。
Java代碼
- Permission printPermission = new PrinterPermission("laserjet4400n", "print");
- Subject currentUser = SecurityUtils.getSubject();
- if (currentUser.isPermitted(printPermission)) {
- //show the Print button
- } else {
- //don't show the button? Grey it out?
- }
- Permission printPermission = new PrinterPermission("laserjet4400n", "print");
- Subject currentUser = SecurityUtils.getSubject();
- if (currentUser.isPermitted(printPermission)) {
- //show the Print button
- } else {
- //don't show the button? Grey it out?
- }
相關方法如下:
Subject方法 | 描述 |
isPermitted(Permission p) | Subject擁有制定權限時,返回treu |
isPermitted(List<Permission> perms) | 返回對應權限的boolean數組 |
isPermittedAll(Collection<Permission> perms) | Subject擁有所有制定權限時,返回true |
2、 基于字符串的實現
相比笨重的基于對象的實現方式,基于字符串的實現便顯得更加簡潔。
Java代碼
- Subject currentUser = SecurityUtils.getSubject();
- if (currentUser.isPermitted("printer:print:laserjet4400n")) {
- //show the Print button
- } else {
- //don't show the button? Grey it out?
- }
使用冒號分隔的權限表達式是org.apache.shiro.authz.permission.WildcardPermission 默認支持的實現方式。
這里分別代表了 資源類型:操作:資源ID
類似基于對象的實現相關方法,基于字符串的實現相關方法:
isPermitted(String perm)、isPermitted(String... perms)、isPermittedAll(String... perms)
基于權限對象的斷言實現
Java代碼
- Subject currentUser = SecurityUtils.getSubject();
- //guarantee that the current user is permitted
- //to open a bank account:
- Permission p = new AccountPermission("open");
- currentUser.checkPermission(p);
- openBankAccount();
基于字符串的斷言實現
Java代碼
- Subject currentUser = SecurityUtils.getSubject();
- //guarantee that the current user is permitted
- //to open a bank account:
- currentUser.checkPermission("account:open");
- openBankAccount();
斷言實現的相關方法
Subject方法 | 說明 |
checkPermission(Permission p) | 斷言用戶是否擁有制定權限 |
checkPermission(String perm) | 斷言用戶是否擁有制定權限 |
checkPermissions(Collection<Permission> perms) | 斷言用戶是否擁有所有指定權限 |
checkPermissions(String... perms) | 斷言用戶是否擁有所有指定權限 |
2、基于注解的授權實現
Shiro注解支持AspectJ、Spring、Google-Guice等,可根據應用進行不同的配置。
相關的注解:
@ RequiresAuthentication
可以用戶類/屬性/方法,用于表明當前用戶需是經過認證的用戶。
Java代碼
- @RequiresAuthentication
- public void updateAccount(Account userAccount) {
- //this method will only be invoked by a
- //Subject that is guaranteed authenticated
- ...
- }
@ RequiresGuest
表明該用戶需為”guest”用戶
@ RequiresPermissions
當前用戶需擁有制定權限
Java代碼
- @RequiresPermissions("account:create")
- public void createAccount(Account account) {
- //this method will only be invoked by a Subject
- //that is permitted to create an account
- ...
- }
@RequiresRoles
當前用戶需擁有制定角色
@ RequiresUser
當前用戶需為已認證用戶或已記住用戶
3、基于JSP TAG的授權實現
Shiro提供了一套JSP標簽庫來實現頁面級的授權控制。
在使用Shiro標簽庫前,首先需要在JSP引入shiro標簽:
Java代碼
- <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
下面一一介紹Shiro的標簽:
guest標簽
驗證當前用戶是否為“訪客”,即未認證(包含未記住)的用戶
Xml代碼
- <shiro:guest>
- Hi there! Please <a href="login.jsp">Login</a> or <a href="signup.jsp">Signup</a> today!
- </shiro:guest>
user標簽
認證通過或已記住的用戶
Xml代碼
- <shiro:user>
- Welcome back John! Not John? Click <a href="login.jsp">here<a> to login.
- </shiro:user>
authenticated標簽
已認證通過的用戶。不包含已記住的用戶,這是與user標簽的區別所在。
Xml代碼
- <shiro:authenticated>
- <a href="updateAccount.jsp">Update your contact information</a>.
- </shiro:authenticated>
notAuthenticated標簽
未認證通過用戶,與authenticated標簽相對應。與guest標簽的區別是,該標簽包含已記住用戶。
Xml代碼
- <shiro:notAuthenticated>
- Please <a href="login.jsp">login</a> in order to update your credit card information.
- </shiro:notAuthenticated>
principal 標簽
輸出當前用戶信息,通常為登錄帳號信息
Xml代碼
- Hello, <shiro:principal/>, how are you today?
hasRole標簽
驗證當前用戶是否屬于該角色
Xml代碼
- <shiro:hasRole name="administrator">
- <a href="admin.jsp">Administer the system</a>
- </shiro:hasRole>
lacksRole標簽
與hasRole標簽邏輯相反,當用戶不屬于該角色時驗證通過
Xml代碼
- <shiro:lacksRole name="administrator">
- Sorry, you are not allowed to administer the system.
- </shiro:lacksRole>
hasAnyRole標簽
驗證當前用戶是否屬于以下任意一個角色。
Xml代碼
- <shiro:hasAnyRoles name="developer, project manager, administrator">
- You are either a developer, project manager, or administrator.
- </shiro:lacksRole>
hasPermission標簽
驗證當前用戶是否擁有制定權限
Xml代碼
- <shiro:hasPermission name="user:create">
- <a href="createUser.jsp">Create a new User</a>
- </shiro:hasPermission>
lacksPermission標簽
與hasPermission標簽邏輯相反,當前用戶沒有制定權限時,驗證通過
Xml代碼
- <shiro:hasPermission name="user:create">
- <a href="createUser.jsp">Create a new User</a>
- </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驗證成功,那么將認為授權成功,立即返回,結束認證。
轉自http://kdboy.iteye.com/blog/1155450