1. 程式人生 > >【Shiro】Apache Shiro架構之許可權認證(Authorization)

【Shiro】Apache Shiro架構之許可權認證(Authorization)

  
  上一篇博文總結了一下Shiro中的身份認證,本文主要來總結一下Shiro中的許可權認證(Authorization)功能,即授權。如下:
授權
  本文參考自Apache Shiro的官方文件:http://shiro.apache.org/authorization.html
  本文遵循以下流程:先介紹Shiro中的許可權認證,再通過一個簡單的例項來具體說明一下API的使用(基於maven)。

1. 許可權認證的核心要素

  許可權認證,也就是訪問控制,即在應用中控制誰能訪問哪些資源。在許可權認證中,最核心的三個要素是:許可權,角色和使用者:

許可權(permission):即操作資源的權利,比如訪問某個頁面,以及對某個模組的資料的新增,修改,刪除,檢視的權利;
角色(role):指的是使用者擔任的的角色,一個角色可以有多個許可權;
使用者(user):在Shiro 中,代表訪問系統的使用者,即上一篇博文提到的Subject認證主體。

  它們之間的的關係可以用下圖來表示:
關係
  一個使用者可以有多個角色,而不同的角色可以有不同的許可權,也可由有相同的許可權。比如說現在有三個角色,1是普通角色,2也是普通角色,3是管理員,角色1只能檢視資訊,角色2只能新增資訊,管理員都可以,而且還可以刪除資訊,類似於這樣。

2.1 基於角色的訪問控制

  也就是說,授權過程是通過判斷角色來完成的,哪個角色可以做這件事,哪些角色可以做這件事等等。它有如下API:

方法 作用
hasRole(String roleName) 判斷是否有該角色訪問權,返回boolen
hasRoles(List<String> roleNames)
判斷是否有這些這些角色訪問權,返回boolean[]
hasAllRoles(Collection<String> roleNames) 判斷是否有這些這些角色訪問權,返回boolean

  對這三個API,做一下簡單的說明,第一個很簡單,傳入一個role即可,判斷是否擁有該角色訪問權,第二個方法是傳入一個role的集合,然後Shiro會根據集合中的每一個role做一下判斷,並且將每次的判斷結果放到boolean[]陣列中,順序與集合中role的順序一致;第三個方法也是傳入一個role的集合,不同的是,返回boolean型別,必須集合中全部role都有才為true,否則為false。
  用法如下:

Subject currentUser = SecurityUtils.getSubject();
if (currentUser.hasRole("administrator")) {
    //show the admin button or do administrator's things
} else {
    //don't show the button?  Grey it out? or others...
}

  除了這三個API外,Shiro還提供了check的API,與上面不同的是,has-xxx會返回boolean型別的資料,用來判斷,而check-xxx不會返回任何東西,如果驗證成功就繼續處理下面的程式碼,否則會丟擲一個異常,可以用來通過捕獲異常來處理。API如下:

方法 作用
checkRole(String roleName) 如果判斷失敗丟擲AuthorizationException異常
checkRoles(String... roleNames) 如果判斷失敗丟擲AuthorizationException異常
checkRoles(Collection<String> roleNames) 如果判斷失敗丟擲AuthorizationException異常

  類似的使用方法如下:

Subject currentUser = SecurityUtils.getSubject();
//guarantee that the current user is a bank teller and
//therefore allowed to open the account:
currentUser.checkRole("bankTeller");
openBankAccount();

2.2 基於許可權的訪問控制

  基於許可權的訪問控制和基於角色的訪問控制在原理上是一模一樣的,只不過API不同而已,我不再做過多的解釋,API如下:

方法 作用
isPermitted(String perm) 判斷是否有該許可權,返回boolen
isPermitted(List<String> perms) 判斷是否有這些這些許可權,返回boolean[]
isPermittedAll(Collection<String> perms) 判斷是否有這些這些許可權,返回boolean
checkPermission(String perm) 如果判斷失敗丟擲AuthorizationException異常
checkPermissions(String... perms) 如果判斷失敗丟擲AuthorizationException異常
checkPermissionsAll(Collection<String> perms) 如果判斷失敗丟擲AuthorizationException異常

3. 許可權認證示例程式碼

  不管是身份認證還是許可權認證,首先都需要建立SecurityManager工廠,SecurityManager,所以首先新建一個工具類專門做這個事情。

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;
    }
}

  提供一個靜態方法,返回當前使用者,在應用程式中我們直接呼叫這個類中的靜態方法即可返回當前認證的使用者了。
  maven中的pom.xml檔案內容和上一節一樣的,不再贅述。
  Shiro的配置檔案shiro.ini:

#使用者,role表示各個角色
[users]
csdn1=123,role1,role2,role3
csdn2=123,role1,role2

#定義不同角色都擁有哪些許可權
[roles]
role1=user:select
role2=user:add,user:update
role3=user.delete

角色認證:

public class RoleTest {

    @Test
    public void testHasRole() {

        String configFile = "classpath:shiro.ini";
        String username = "csdn2";
        String password = "123";
        Subject currentUser = ShiroUtil.login(configFile, username, password);

        //測試hasRole
        System.out.println(currentUser.hasRole("role2")? "有role2這個角色" : "沒有role2這個角色");

        //測試hasRoles
        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","role3")));

        currentUser.logout();
    }

    @Test
    public void testCheckRole() {

        String configFile = "classpath:shiro.ini";
        String username = "csdn2";
        String password = "123";
        Subject currentUser = ShiroUtil.login(configFile, username, password);

//      currentUser.checkRole("role3");//沒有返回值。有就不報錯,沒有就會報錯
//      currentUser.checkRoles(Arrays.asList("role1","role2","role3")); //同上
        currentUser.checkRoles(Arrays.asList("role1","role2")); //同上

        currentUser.logout();
    }
}

  許可權認證和角色認證的測試一樣的,我就不再贅述了。當然了,這裡只是單純的測試,實際中,認證完了後還要做一些具體的業務邏輯處理。

文末福利:“程式設計師私房菜”,一個有溫度的公眾號~
程式設計師私房菜