1. 授權
授權即訪問控制,控制誰能訪問哪些資源。主體進行身份認證后需要分配權限方可訪問系統的資源,對於某些資源沒有權限是無法訪問的。
2. 關鍵對象
授權可簡單理解為 who 對 what(which)進行 How 操作;
Who,即主體(Subject),主體需要訪問系統中的資源;
What,即資源(Resource),如系統菜單、頁面、按鈕、類方法、系統商品信息等。資源包括資源類型和資源實例,比如商品信息為資源類型,類型為 t01 的商品為資源實例,編號 001 的商品也屬於資源實例;
How,權限 / 許可(Permission),規定了主體對資源的操作許可, 權限離開資源沒有意義,如用戶查詢權限、用戶添加權限、某個類方法的調用權限、編號為 001 用戶的修改權限等,通過權限可知主體對哪些資源都有哪些操作許可。
3. 授權流程
4. 授權方式
- 基於角色的訪問控制【RBAC 基於角色的訪問控制(Role-Based Access Control)是以角色為中心進行訪問控制】
if(subject.hasRole("admin")){ //操作什么資源 }
- 基於資源的訪問控制【RBAC 基於資源的訪問控制(Resource-Based Access Control)是以資源為中心進行訪問控制】
if(subject.isPermission("user:update:01")){ //資源實例 //對01用戶進行修改 } if(subject.isPermission("user:update:*")){ //資源類型 //對01用戶進行修改 }
5. 權限字符串
權限字符串的規則是【資源標識符:操作:資源實例標識符】意思是對哪個資源的哪個實例具有什么操作,“:”是資源/操作/實例的分割符,權限字符串也可以使用 * 通配符。
例子:
-
用戶創建權限:user : create,或 user : create : *
-
用戶修改實例001的權限:user : update : 001
-
用戶實例001的所有權限:user : *:001
6. 開發授權代碼
a 自定義 Realm 代碼實現
public class CustomerMD5Realm extends AuthorizingRealm { /** * 一個主題可以有多個身份,參數是集合 principals;但是只有一個主身份; * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // 獲取身份信息 String principal = (String) principals.getPrimaryPrincipal(); System.out.println("身份信息 = " + principal); // 根據身份信息 用戶名 獲取當前用戶的角色信息,以及權限信息【比如:從數據庫中獲取】 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); // 將數據庫中查詢角色信息賦值給權限對象 simpleAuthorizationInfo.addRoles(Arrays.asList("admin", "user")); simpleAuthorizationInfo.addStringPermission("user:*:01"); simpleAuthorizationInfo.addStringPermission("product:*:*"); return simpleAuthorizationInfo; } // 認證 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String principal = (String) token.getPrincipal(); System.out.println("principal = " + principal); return null; } }
b 授權處理【會自動調用自定義 CustomerMD5Realm 的 doGetAuthorizationInfo 獲取指定用戶的權限信息】
public class TestCustomerMD5Authenticator { public static void main(String[] args) { // 1. 創建安全管理器 DefaultSecurityManager securityManager = new DefaultSecurityManager(); // 2. 給安全管理器設置 realm 【Realm 需要獲取用戶認證的數據源信息】 CustomerMD5Realm customerMD5Realm = new CustomerMD5Realm(); // 設置 realm 使用的 hash 憑證匹配器 HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); credentialsMatcher.setHashAlgorithmName("md5"); // 設置使用的 Hash 算法 credentialsMatcher.setHashIterations(1024); // 需要散列多少次 customerMD5Realm.setCredentialsMatcher(credentialsMatcher); securityManager.setRealm(customerMD5Realm); // 3. SecurityUtils 設置安全管理器 SecurityUtils.setSecurityManager(securityManager); // 4. 獲取關鍵對象 Subject 主體 Subject subject = SecurityUtils.getSubject(); // 5. 創建令牌 UsernamePasswordToken token = new UsernamePasswordToken("xiaochen", "123"); // 用戶認證 try { System.out.println("認證狀態: " + subject.isAuthenticated()); subject.login(token); System.out.println("認證狀態: " + subject.isAuthenticated()); } catch (UnknownAccountException e) { e.printStackTrace(); System.out.println("認證失敗: 用戶名不存在~"); } catch (IncorrectCredentialsException e) { e.printStackTrace(); System.out.println("認證失敗: 密碼錯誤~"); } // 授權 if (subject.isAuthenticated()) { //基於角色權限控制 System.out.println(subject.hasRole("super")); // 走授權流程 // 基於多角色權限控制 System.out.println(subject.hasAllRoles(Arrays.asList("admin", "user"))); // 走授權流程 // 是否具有其中一個角色 boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "user", "super")); // 走授權流程 for (boolean aBoolean : booleans) { System.out.println(aBoolean); } // 基於權限字符串的訪問控制 資源標識符:操作:資源類型 System.out.println(subject.isPermitted("user:update:01")); // 走授權流程 // 分別具有哪些權限 boolean[] permitted = subject.isPermitted("user:*:01", "order:*:02"); // 走授權流程 for (boolean b : permitted) { System.out.println(b); } } } }
注意:深入了解 Shiro 的授權流程,可以 debug 運行程序,查看源代碼進行學習深究!