深入淺出Shiro系列——權限認證


寫在前面:

小夥伴兒們,大家好!上一篇我們學了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,授權流程

流程如下:

  1. 首先調用 Subject.isPermitted*/hasRole*接口,其會委託給 SecurityMana ger,而 SecurityManager 接着會委託給 Authorizer;

  2. Authorizer 是真正的授權者,如果我們調用如 isPermitted(“user:view”),其首先會通過 PermissionResolver 把字符串轉換成相應的 Permission 實例;

  3. 在進行授權之前,其會調用相應的 Real m 獲取 Subject 相應的角色/權限用於匹配傳入的角色/權限;

  4. Authorizer 會判斷 Realm 的角色/權限是否和傳入的匹配,如果有多個 Real m,會委託給 ModularRealmAuthori zer 進行循環判斷,如果匹配如 isPermitted*/hasRole* 會返回 true,否則返回 false 表示授權失敗。


文中源代碼已上傳至碼雲:

https://gitee.com/Huke-123/shiro_series

好了,今天就先分享到這裏了,下期繼續給大家帶來Shiro相關方面的學習!更多幹貨、優質文章,歡迎關注我的原創技術公衆號~

你點的每一個在看,我都認真當成了喜歡

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章