授權即存取控制,它將判斷使用者在應用程式中對資源是否擁有相應的存取權限。
如,判斷一個使用者有檢視頁面的權限,編輯資料的權限,擁有某一按鈕的權限,以及是否擁有列印的權限等等。
一、授權的三要素
授權有三個核心元素:權限、角色和使用者。
權限
權限是Apache Shiro安全機制最核心的元素。它在應用程式中明確聲明了被允許的行為和表現。一個格式良好好的權限聲明可以清楚表達出使用者對該資源擁有的權限。
大多數的資源會支援典型的CRUD操作(create,read,update,delete),但是任何操作建立在特定的資源上才是有意義的。因此,權限聲明的根本想法就是建立在資源以及操作上。
而我們透過權限聲明僅能了解這個權限可以在應用程式中做些什麼,而不能確定誰擁有此權限。
於是,我們需要在應用程式中對使用者和權限建立關聯。
通常的做法就是將權限指派給某個角色,然後將這個角色關聯一個或多個使用者。
權限宣告及粒度
Shiro權限宣告通常是使用以冒號分隔的表達式。就像前文所講,一個權限表達式可以清晰的指定資源類型,允許的操作,可存取的資料。同時,Shiro權限表達式支援簡單的通配符,可以更靈活的進行權限設定。
下面以實例來說明權限表達式。
可查詢使用者資料
User:view
可查詢或編輯使用者資料
User:view,edit
可查詢或編輯使用者資料
User:view,edit
可對使用者使用者資料
User:edit:123
角色
Shiro支援兩種角色模式:
1、傳統角色:一個角色代表一系列的操作,當需要授權給某一操作進行驗證時,只需判斷是否是該角色即可。這種角色權限相對簡單、模糊,不利於擴充。
2、權限角色:一個角色擁有一個權限的集合。授權驗證時,需要判斷目前角色是否擁有該權限。這種角色權限可以對該角色進行詳細的權限描述,適合更複雜的權限設計。
以下將詳細描述兩種角色模式的授權實作。
二、授權實作
Shiro支援三種方式實現授權流程:
編碼實作
註解實作
JSP Taglig實作
1、基於編碼的授權實現。當使用者是否擁有某個角色時,可以呼叫Subject 實例的hasRole*方法驗證。
Subject currentUser = SecurityUtils.getSubject(); if (currentUser.hasRole("administrator")) { //show the admin button } else { //don't show the button? Grey it out? }
Subject方法
hasRoles(List
hasAllRoles(Collection
斷言支援
Shiro也支援以斷言驗證的方式進行授權驗證。斷言成功,不傳回任何值,程式繼續執行;斷言失敗時,將拋出異常訊息。使用斷言,可以使我們的程式碼更加簡潔。
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方法
checkRoles(Collection
checkRoles(String.. . roleNames) 對上一方法的方法重載
1.2 基於權限角色授權實現
相比傳統角色模式,基於權限的角色模式耦合性要更低些,它不會因角色的改變而對源代碼進行修改,因此,基於權限的角色模式是更好的存取控制方式。
它的程式碼實作有以下幾種實作方式:
1、基於權限物件的實作
建立org.apache.shiro.authz.Permission的實例,將該實例物件作為參數傳遞給Subject.isPermitted()進行驗證。
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? }
isPermitted(List
isPermittedAll(Collection
2、 基于字符串的实现
相比笨重的基于对象的实现方式,基于字符串的实现便显得更加简洁。
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)
基于权限对象的断言实现
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();
基于字符串的断言实现
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
checkPermissions(String... perms) 断言用户是否拥有所有指定权限
2、基于注解的授权实现
Shiro注解支持AspectJ、Spring、Google-Guice等,可根据应用进行不同的配置。
相关的注解:
@ RequiresAuthentication
可以用户类/属性/方法,用于表明当前用户需是经过认证的用户。
@RequiresAuthentication public void updateAccount(Account userAccount) { //this method will only be invoked by a //Subject that is guaranteed authenticated ... } @ RequiresGuest
表明该用户需为”guest”用户
@ RequiresPermissions
当前用户需拥有制定权限
@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标签:
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
下面一一介绍Shiro的标签:
guest标签
验证当前用户是否为“访客”,即未认证(包含未记住)的用户
<shiro:guest> Hi there! Please <a href="login.jsp">Login</a> or <a href="signup.jsp">Signup</a> today! </shiro:guest>
user标签
认证通过或已记住的用户
<shiro:user> Welcome back John! Not John? Click <a href="login.jsp">here<a> to login. </shiro:user>
authenticated标签
已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。
<shiro:authenticated> <a href="updateAccount.jsp">Update your contact information</a>. </shiro:authenticated> notAuthenticated标签
未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。
<shiro:notAuthenticated> Please <a href="login.jsp">login</a> in order to update your credit card information. </shiro:notAuthenticated>
principal 标签
输出当前用户信息,通常为登录帐号信息
Hello, <shiro:principal/>, how are you today?
验证当前用户是否属于该角色
<shiro:hasRole name="administrator"> <a href="admin.jsp">Administer the system</a> </shiro:hasRole>
lacksRole标签
与hasRole标签逻辑相反,当用户不属于该角色时验证通过
<shiro:lacksRole name="administrator"> Sorry, you are not allowed to administer the system. </shiro:lacksRole>
hasAnyRole标签
验证当前用户是否属于以下任意一个角色。
<shiro:hasAnyRoles name="developer, project manager, administrator"> You are either a developer, project manager, or administrator. </shiro:lacksRole>
hasPermission标签
验证当前用户是否拥有制定权限
<shiro:hasPermission name="user:create"> <a href="createUser.jsp">Create a new User</a> </shiro:hasPermission>
lacksPermission标签
与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过
<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验证成功,那么将认为授权成功,立即返回,结束认证。
以上就是Apache Shiro 使用手册(三)Shiro 授权的内容,更多相关内容请关注PHP中文网(www.php.cn)!