授權,也稱訪問控制,在應用中控制誰能訪問哪些資源(如訪問頁面、編輯數據、等)。在授權中需瞭解的幾個關鍵詞:主體(subject)、資源(resource)、權限(permission)、角色(role)。
主體
主體,訪問應用的用戶,在 Shiro 中使用 Subject 代表該用戶。用戶只有授權後才允許訪問相應的資源。
資源
在應用中用戶可以訪問的任何東西,比如訪問 JSP 頁面、CRUD數據、訪問接口、打印等等都是資源。用戶只有通過授權後才能訪問。
權限
通過權限我們可以表示在應用中用戶有沒有操作某個資源的權力。即權限表示在應用中用戶能不能訪問某個資源,如: 訪問用戶列表頁面、CRUD數據、打印文檔等。
角色
角色代表了用戶操作權限集合。一般情況下我們會賦予用戶角色而不是權限,即這樣用戶可以擁有多個權限。如:項目經理、技術總監、部門經理、主管等都是角色,不同的角色擁有多個不同的權限。
隱式角色:
直接通過角色來驗證用戶有沒有操作權限,在實際應用中技術總監、主管可以使用打印機,假設某天不允許主管使用打印機,此時需要從應用中刪除相應邏輯代碼。粒度較粗。
顯示角色:
在程序中通過權限控制誰能訪問某個資源,即角色擁有多個權限,這樣假設哪個角色不能訪問某個資源,只需要從角色擁有權限中移除即可。無須修改多處代碼,即粒度是以資源-實例為單位的;粒度較細。
1)編程式:通過寫 if/else 授權代碼塊完成。
//1. 從 PrincipalCollection 中來獲取登錄用戶的信息
Object principal = arg0.getPrimaryPrincipal();
//2. 利用登錄的用戶的信息來獲取當前用戶的角色或權限
Set<string> roles = new HashSet<>();/<string>
roles.add("user");
if("admin".equals(principal)){
roles.add("admin");
}
/
2)註解式:通過在執行的方法上註解完成。
@RequiresRoles("user")
public void user() {
//有權限
}
3)JSP標籤:在 JSP頁面通過相應的標籤完成。(jsp已過時了,現在是前後端分離)
<hasrole>
1) 基於角色的訪問控制。
在 ini 配置文件配置用戶擁有的角色,格式:用戶名=密碼,角色1,角色2。
[users]
zhang=123456,role1,role2
wang=123456,role1
Shiro 提供了 hasRole/hasAllRoles用於判斷用戶是否擁有某個角色。
// 1、獲取SecurityManager工廠,此處使用Ini配置文件初始化SecurityManager
Factory<org.apache.shiro.mgt.securitymanager> factory =/<org.apache.shiro.mgt.securitymanager>
new IniSecurityManagerFactory("classpath:shiro.ini");
// 2、得到SecurityManager實例 並綁定給SecurityUtils
org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
// 3、得到Subject及創建用戶名/密碼身份驗證Token
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123456");
try {
// 4、登錄,身份驗證
subject.login(token);
} catch (AuthenticationException e) {
// 5、身份驗證失敗
logger.info("用戶名 、密碼錯誤");
}
if(subject.hasRole("role1")){
logger.info(token.getUsername()+" has role1");
}else{
logger.info(token.getUsername()+" hasn't role1");
}
2) 基於資源(權限)的訪問控制
在 ini 配置文件配置用戶擁有的角色及角色-權限關係,格式:"角色=權限 1,權限 2"。
首先根據用戶名找到角色,然後根據角色再找到權限,即角色是權限的組合。
[users]
zhang=123456,role1,role2
wang=123456,role1
[roles]
role1=user:create,user:update
role2=user:create,user:delete
Shiro 提供了 isPermitted 和 isPermittedAll 用於判斷用戶是否擁有某個權限或所有權限。
if(subject.isPermitted("user:create")){
logger.info(token.getUsername()+" has user:create");
}else{
logger.info(token.getUsername()+" hasn't user:create");
}
1)首先調用 subject.isPermitted*/hasRole*接口,其會委託給 SecurityManager,而 SecurityManager 接著會委託給 Authorizer。
2)Authorizer 是真正的授權者,如果我們調用如 isPermitted("user:view"),其首先會通過 PermissionResolver 把字符串轉換成相應的 Permission 實例;
在進行授權之前,其會調用相應的 Realm 獲取 Subject 相應的角色、權限用於匹配傳入的角色、權限;
3) Authorizer 會判斷 Realm 的角色、權限是否和傳入的匹配,如果有多個 Realm,會委託給 ModularRealmAuthorizer 進行循環判斷,如果匹配如 isPermitted*/hasRole* 會返回 true,否則返回 false 表示授權失敗。
ModularRealmAuthorizer 進行多 Realm 匹配流程
1)首先檢查相應的 Realm 是否實現了實現了 Authorizer;
2)如果實現了 Authorizer,那麼接著調用其相應的 isPermitted*/hasRole* 接口進行匹配;
3)如果有一個 Realm 匹配那麼將返回 true,否則返回 false。
1、自定義類JdbcRealm繼續父AuthorizingRealm,重寫doGetAuthorizationInfo授權方法,根據授權的用戶在數據庫中查詢所擁有的角色、權限。
2、 當我們調用hasRole("普通員工")方法時,Authorizer 會調用doGetAuthorizationInfo()方法進行授權,判斷 Realm 的角色、權限是否和傳入的匹配,如果匹配,hasRole("普通員工")返回true,否則返回false。
3、 測試結果
閱讀更多 區塊鏈程序猿 的文章