shiro授權流程分析

       上一篇博客,寫了 shiro認證流程分析 ,shiro主要功能是認證和授權,接下來,看看授權是如何進行的,既然要授權,當然會執行我們自定義的授權方法,所以在授權方法打個斷點看看情況,當訪問需要角色或者權限的接口時,就會來到授權方法(這裏暫時不考慮緩存的因素,可以看做是第一次請求):

我們看一下方法調用棧:

 

      可以看到控制器AdminController是Cglib的一個代理對象,這裏會涉及到aop東西,我會適當的跳過,不影響理解shiro的授權流程。我們直接跳到方法調用棧中DelegatingSubject的checkRole()方法,從這裏開始看:

    public void checkRole(String role) throws AuthorizationException {
        assertAuthzCheckPossible();
        //檢查主體是否擁有role這個角色,關注這個
        securityManager.checkRole(getPrincipals(), role);
    }
來到AuthorizingSecurityManager的checkRole()方法:
    public void checkRole(PrincipalCollection principals, String role) throws AuthorizationException {
        this.authorizer.checkRole(principals, role);
    }

這裏的授權器authorizer是ModularRealmAuthorizer類型的,從構造函數可以知道:

所以接下來調用ModularRealmAuthorizer的checkRole()方法:

    public void checkRole(PrincipalCollection principals, String role) throws AuthorizationException {
        assertRealmsConfigured();
        if (!hasRole(principals, role)) {
            throw new UnauthorizedException("Subject does not have role [" + role + "]");
        }
    }

而checkRole又通過hasRole來判斷:

    public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
        assertRealmsConfigured();
        for (Realm realm : getRealms()) {
            if (!(realm instanceof Authorizer)) continue;
            if (((Authorizer) realm).hasRole(principals, roleIdentifier)) {
                return true;
            }
        }
        return false;
    }

可以發現,hasRole方法中,會先獲取到所有的realm,判斷是否屬於Authorizer類型的,如果是,則強轉爲Authorizer類型,並調用realm的hasRole方法,我們自定義的realm就是Authorizer類型的,所以會被調用

 

來到AuthorizingRealm的hasRole方法:

    public boolean hasRole(PrincipalCollection principal, String roleIdentifier) {
	    //獲取授權信息 
        AuthorizationInfo info = getAuthorizationInfo(principal);
		//判斷info中是否有roleIdentifier這個角色
        return hasRole(roleIdentifier, info);
    }

它會分兩步進行:

(1)獲取授權信息

(2)判斷授權信息中,是否包含所需要的角色(因爲這裏是以角色 爲例,如果是權限的話,就會判斷是否有權限)

一個個看:

(1)獲取授權信息:

    //獲取授權信息 
    protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {

        if (principals == null) {
            return null;
        }

        AuthorizationInfo info = null;

        if (log.isTraceEnabled()) {
            log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]");
        }

        Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache();
        if (cache != null) {
            if (log.isTraceEnabled()) {
                log.trace("Attempting to retrieve the AuthorizationInfo from cache.");
            }
            Object key = getAuthorizationCacheKey(principals);
            info = cache.get(key);
            if (log.isTraceEnabled()) {
                if (info == null) {
                    log.trace("No AuthorizationInfo found in cache for principals [" + principals + "]");
                } else {
                    log.trace("AuthorizationInfo found in cache for principals [" + principals + "]");
                }
            }
        }


        if (info == null) {
            // Call template method if the info was not found in a cache
            info = doGetAuthorizationInfo(principals);
            // If the info is not null and the cache has been created, then cache the authorization info.
            if (info != null && cache != null) {
                if (log.isTraceEnabled()) {
                    log.trace("Caching authorization info for principals: [" + principals + "].");
                }
                Object key = getAuthorizationCacheKey(principals);
                cache.put(key, info);
            }
        }

        return info;
    }

這個思路和獲取認證信息是一樣的:

a) 先從緩存找,如果有,則返回

b)緩存沒有,則執行 info = doGetAuthorizationInfo(principals);獲取

第一次獲取緩存,肯定是沒有的,所以會走 info = doGetAuthorizationInfo(principals);這個方法,這樣就會調用我們自定義的授權方法了:

    //授權
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        User user = (User)SecurityUtils.getSubject().getPrincipal();  //獲取當前登錄的用戶信息

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //這裏模擬數據庫,進行角色和權限的處理
        //這裏只是簡單模擬,角色使用是用戶名,權限是包含用戶名的路徑
        simpleAuthorizationInfo.addRole(user.getUsername());
        simpleAuthorizationInfo.addStringPermission("/"+user.getUsername());

        return simpleAuthorizationInfo;
    }

(2)判斷是否擁有某個角色

這個很簡單,就是獲取到所有的角色集合,看看是否包含要的角色:

    protected boolean hasRole(String roleIdentifier, AuthorizationInfo info) {
	    //info.getRoles()返回的是一個集合
        return info != null && info.getRoles() != null && info.getRoles().contains(roleIdentifier);
    }

這樣子,也就知道了當前主體是否包含某個角色信息,有的話,就有權訪問,否則就是沒有權限訪問。

 

 

 

 

 

 

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