Shiro 授權流程詳解


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 運行程序,查看源代碼進行學習深究!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM