寫在前面:
小夥伴兒們,大家好!上一篇我們學了Shiro的身份認證——深入淺出Shiro系列——身份認證
這次讓我們一起來學習Shiro權限認證!
思維導圖:
1,Shiro 授權
授權,也叫訪問控制,即在應用中控制誰能訪問哪些資源(如訪問頁面/編輯數據/頁面操作等)。在授權中需瞭解的幾個關鍵對象:主體(Subject)、資源(Resou rce)、權限(Permission)、角色(Role )。
主體:主體,即訪問應用的用戶,在Shiro中使用 Subject 代表該用戶。用戶只有授權後才允許訪問相應的資源。
資源:在應用中用戶可以訪問的任何東西,比如訪問 JSP 頁面、查看/編輯某些數據、訪問某個業務方法、打印文本等等都是資源。用戶只要授權後才能訪問。
權限:安全策略中的原子授權單位,通過權限我們可以表示在應用中用戶有沒有操作某個資源的權力。即權限表示在應用中用戶能不能訪問某個資源,如:訪問用戶列表頁面,查看/新增/修改/刪除用戶數據(即很多時候都是 CRUD(增查改刪)式權限控制)等。
如上可以看出,權限代表了用戶有沒有操作某個資源的權利,即反映在某個資源上的操作允不允許,不反映誰去執行這個操作。所以後續還需要把權限賦予給用戶,即定義哪個用戶允許在某個資源上做什麼操作(權限),Shiro 不會去做這件事情,而是由實現人員提供。
角色:角色代表了操作集合,可以理解爲權限的集合,一般情況下我們會賦予用戶角色而不是權限,即這樣用戶可以擁有一組權限,賦予權限時比較方便。典型的如:項目經理、技術總監、CTO、開發工程師等都是角色,不同的角色擁有一組不同的權限。
2,授權方式
Shiro 支持三種方式的授權:編程式,註解式和標籤式;這裏講一下編程式。
這裏我們先將Shiro認證封裝成一個工具類ShiroUtil;
package com.java.common;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
/**
* ShiroUtil工具類
*/
public class ShiroUtil {
public static Subject login(String configFile,String userName,String password){
// 讀取配置文件,初始化SecurityManager工廠
Factory<SecurityManager> factory=new IniSecurityManagerFactory(configFile);
// 獲取securityManager實例
SecurityManager securityManager=factory.getInstance();
// 把securityManager實例綁定到SecurityUtils
SecurityUtils.setSecurityManager(securityManager);
// 得到當前執行的用戶
Subject currentUser=SecurityUtils.getSubject();
// 創建token令牌,用戶名/密碼
UsernamePasswordToken token=new UsernamePasswordToken(userName, password);
try{
// 身份認證
currentUser.login(token);
System.out.println("身份認證成功!");
}catch(AuthenticationException e){
e.printStackTrace();
System.out.println("身份認證失敗!");
}
return currentUser;
}
}
來看看程序結構:
編程式;
基於角色的訪問控制;
配置文件shiro_role.ini:
[users]
java=123,role1,role2
jack=123,role1
規則即:“用戶名=密碼,角色1,角色2”,如果需要在應用中判斷用戶是否有相應角色,就需要在相應的 Realm 中返回角色信息,也就是說 Shiro 不負責維護用戶-角色信息,需要應用提供。
測試類:
package com.java.shiro;
import com.java.common.ShiroUtil;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
import java.util.Arrays;
public class RoleTest {
@Test
public void testHasRole() {
Subject currentUser= ShiroUtil.login("classpath:shiro_role.ini", "java", "123");
// Subject currentUser=ShiroUtil.login("classpath:shiro_role.ini", "jack", "123");
//hasRole()方法判斷currentUser用戶是否有role1和role2角色
System.out.println(currentUser.hasRole("role1")?"有role1這個角色":"沒有role1這個角色");
//hasRoles()方法同時判斷currentUser用戶是否有多個角色
boolean []results=currentUser.hasRoles(Arrays.asList("role1","role2","role3"));
System.out.println(results[0]?"有role1這個角色":"沒有role1這個角色");
System.out.println(results[1]?"有role2這個角色":"沒有role2這個角色");
System.out.println(results[2]?"有role3這個角色":"沒有role3這個角色");
//hasAllRoles()方法判斷是否擁有所有角色
System.out.println(currentUser.hasAllRoles(Arrays.asList("role1","role2"))?"role1,role2這兩個角色都有":"role1,role2這個兩個角色不全有");
//currentUser.logout();
}
}
輸出結果:
基於權限的訪問控制;
配置文件shiro_permission.ini:
[users]
java=123,role1,role2
jack=123,role1
[roles]
role1=user:select
role2=user:add,user:update,user:delete
規則:“用戶名=密碼,角色 1,角色 2”“角色=權限 1,權限 2”,即首先根據用戶名找到角色,然後根據角色再找到權限;即角色是權限集合;Shiro 同樣不進行權限的維護,需要我們通過 Realm 返回相應的權限信息。只需要維護“用戶——角色”之間的關係即可。
測試類:
package com.java.shiro;
import com.java.common.ShiroUtil;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
public class PermissionTest {
@Test
public void testIsPermitted() {
Subject currentUser=ShiroUtil.login("classpath:shiro_permission.ini", "java", "123");
// Subject currentUser=ShiroUtil.login("classpath:shiro_permission.ini", "jack", "123");
System.out.println(currentUser.isPermitted("user:select")?"有user:select這個權限":"沒有user:select這個權限");
System.out.println(currentUser.isPermitted("user:update")?"有user:update這個權限":"沒有user:update這個權限");
boolean results[]=currentUser.isPermitted("user:select","user:update","user:delete");
System.out.println(results[0]?"有user:select這個權限":"沒有user:select這個權限");
System.out.println(results[1]?"有user:update這個權限":"沒有user:update這個權限");
System.out.println(results[2]?"有user:delete這個權限":"沒有user:delete這個權限");
System.out.println(currentUser.isPermittedAll("user:select","user:update")?"有user:select,update這兩個權限":"user:select,update這兩個權限不全有");
currentUser.logout();
}
}
輸出結果:
到此基於資源的訪問控制(顯示角色)就完成了,也可以叫基於權限的訪問控制,這種方式的一般規則是“資源標識符:操作”,即是資源級別的粒度;這種方式的好處就是如果要修改基本都是一個資源級別的修改,不會對其他模塊代碼產生影響,粒度小。但是實現起來可能稍微複雜點,需要維護“用戶——角色,角色——權限(資源:操作)”之間的關係。
3,授權流程
流程如下:
首先調用
Subject.isPermitted*/hasRole*
接口,其會委託給 SecurityMana ger,而 SecurityManager 接着會委託給 Authorizer;Authorizer 是真正的授權者,如果我們調用如 isPermitted(“user:view”),其首先會通過 PermissionResolver 把字符串轉換成相應的 Permission 實例;
在進行授權之前,其會調用相應的 Real m 獲取 Subject 相應的角色/權限用於匹配傳入的角色/權限;
Authorizer 會判斷 Realm 的角色/權限是否和傳入的匹配,如果有多個 Real m,會委託給 ModularRealmAuthori zer 進行循環判斷,如果匹配如
isPermitted*/hasRole*
會返回 true,否則返回 false 表示授權失敗。
文中源代碼已上傳至碼雲:
https://gitee.com/Huke-123/shiro_series
好了,今天就先分享到這裏了,下期繼續給大家帶來Shiro相關方面的學習!更多幹貨、優質文章,歡迎關注我的原創技術公衆號~
你點的每一個在看,我都認真當成了喜歡