SHIRO源碼解讀——Authorizer權(quán)限校驗(yàn)

權(quán)限控制可以描述為:“誰”對(duì)“什么”進(jìn)行什么”操作"。通過權(quán)限控制,我們可以有效控制用戶的行為操作,進(jìn)而做到對(duì)資源的有效保護(hù)。

一、SHIRO授權(quán)示例

shiro支持兩種兩種訪問控制方式,分別為:基于角色的訪問控制、基于資源的訪問控制。

基于角色的訪問控制:

Subject subject = SecurityUtils.getSubject();
subject.hasRole("tole1");
subject.hasAllRoles("role1","role2");

基于資源的訪問控制:

Subject subject = SecurityUtils.getSubject();
subject.isPermitted("user:create");
subject.isPermittedAll("user:update","user:delete");

下面本文將對(duì)shiro的內(nèi)部授權(quán)邏輯過程進(jìn)行詳細(xì)分析。

二、SHIRO授權(quán)過程

“基于角色的訪問控制邏輯”和“基于資源的訪問控制邏輯”內(nèi)部過程基本是一樣的,因此本文分析只分析下“基于資源的訪問控制邏輯”的過程。

使用者調(diào)用Subject.isPermitted方法進(jìn)行授權(quán),isPermitted接口實(shí)現(xiàn)是在DelegatingSubject中,代碼如下:

public class DelegatingSubject implements Subject {
    public boolean isPermitted(String permission) {
        return hasPrincipals() && securityManager.isPermitted(getPrincipals(), permission);
    }

    public boolean isPermitted(Permission permission) {
        return hasPrincipals() && securityManager.isPermitted(getPrincipals(), permission);
    }

    public boolean[] isPermitted(String... permissions) {
        if (hasPrincipals()) {
            return securityManager.isPermitted(getPrincipals(), permissions);
        } else {
            return new boolean[permissions.length];
        }
    }
    ...
}

從代碼可以發(fā)現(xiàn),DelegatingSubject又委托給了SecurityManager,調(diào)用SecurityManager中的isPermitted方法。下面看看SecurityManager的授權(quán)處理。

首先看看shiro核心邏輯類圖。


Shiro核心邏輯類圖

SecurityManager其實(shí)是一個(gè)代理,SecurityManager代理了Authorizer,SecurityManager子類AuthorizingSecuritymanager為具體代理子類,看看類中相應(yīng)方法定義:

public boolean isPermitted(PrincipalCollection principals, String permissionString) {
        return this.authorizer.isPermitted(principals, permissionString);
    }

public boolean isPermitted(PrincipalCollection principals, Permission permission) {
    return this.authorizer.isPermitted(principals, permission);
}

public boolean[] isPermitted(PrincipalCollection principals, String... permissions) {
    return this.authorizer.isPermitted(principals, permissions);
}

...

確實(shí)是代理模式,類中真正校驗(yàn)是Authorizer做的,具體Authorizer為ModularRealmAuthorizer,ModularRealmAuthorizer中方法定義如下:

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

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

從上面代碼又可以看出,ModularRealmAuthorizer又把校驗(yàn)的工作交給了Realm,授權(quán)的Realm為AuthorizingRealm,AuthorizingRealm中實(shí)現(xiàn)了isPermitted方法,方法定義如下:


public boolean isPermitted(PrincipalCollection principals, String permission) {
    Permission p = getPermissionResolver().resolvePermission(permission);
    return isPermitted(principals, p);
}

public boolean isPermitted(PrincipalCollection principals, Permission permission) {
    AuthorizationInfo info = getAuthorizationInfo(principals);
    return isPermitted(permission, info);
}

如果是字符串Permission,首先要進(jìn)行處理,處理過程如下:
PermissionResolver對(duì)字符串權(quán)限進(jìn)行轉(zhuǎn)化處理,其繼承類為WildcardPermissionResolver.

List<String> parts = CollectionUtils.asList(wildcardString.split(PART_DIVIDER_TOKEN));

上面這句代碼首先將權(quán)限按 " : "進(jìn)行分割成數(shù)組。然后對(duì)這個(gè)數(shù)組中的每個(gè)元素再按照 ”,“ 分割。

Set<String> subparts = CollectionUtils.asSet(part.split(SUBPART_DIVIDER_TOKEN));

最終返回形式為List<Set<String>>。

那么轉(zhuǎn)化后怎么用了,看看WildcardPermission的繼承關(guān)系:

public class WildcardPermission implements Permission, Serializable{
......
}

而Permission的定義如下:

public interface Permission {
   boolean implies(Permission p);
}

WildcardPermission的implices方法實(shí)現(xiàn)如下:

public boolean implies(Permission p) {
    if (!(p instanceof WildcardPermission)) {
        return false;
    }
    WildcardPermission wp = (WildcardPermission) p;
    List<Set<String>> otherParts = wp.getParts();

    int i = 0;
    for (Set<String> otherPart : otherParts) {
        if (getParts().size() - 1 < i) {
            return true;
        } else {
            Set<String> part = getParts().get(i);
            if (!part.contains(WILDCARD_TOKEN) && !part.containsAll(otherPart)) {
                return false;
            }
            i++;
        }
    }

    // If this permission has more parts than the other parts, only imply it if all of the other parts are wildcards
    for (; i < getParts().size(); i++) {
        Set<String> part = getParts().get(i);
        if (!part.contains(WILDCARD_TOKEN)) {
            return false;
        }
    }

    return true;
}

這個(gè)代碼的意思就是instance的權(quán)限是否包含目標(biāo)權(quán)限,如果包含,那么就鑒權(quán)成功,返回True,否則鑒權(quán)不通過,這樣就完成了權(quán)限校驗(yàn)。

我們需要自定義授權(quán)邏輯時(shí),繼承AuthorizingRealm,然后自定義實(shí)現(xiàn)其授權(quán)方法即可!

三、總結(jié)

總結(jié)一下,權(quán)限校驗(yàn)流程如下:


SHIRO授權(quán)

具體流程為:
1)首先調(diào)用Subject.isPermitted/hasRole接口,其會(huì)委托給SecurityManager,而SecurityManager接著會(huì)委托給Authorizer(SecurityManager繼承了Authorizer接口,其真正業(yè)務(wù)邏輯還是委托給相應(yīng)的Authorizer實(shí)例。ModularRealmAuthenticator是Authenticator的一個(gè)實(shí)現(xiàn)類,一般主要用這個(gè)實(shí)現(xiàn)類)。
2)Authorizer是真正的授權(quán)者,如果我們調(diào)用如isPermitted(“user:view”),其首先會(huì)通過PermissionResolver把字符串轉(zhuǎn)換成相應(yīng)的Permission實(shí)例;
3)在進(jìn)行授權(quán)之前,其會(huì)調(diào)用相應(yīng)的Realm獲取Subject相應(yīng)的角色/權(quán)限用于匹配傳入的角色/權(quán)限;
4)Authorizer會(huì)判斷Realm的角色/權(quán)限是否和傳入的匹配,如果有多個(gè)Realm,會(huì)委托給ModularRealmAuthorizer進(jìn)行循環(huán)判斷,如果匹配如isPermitted/hasRole會(huì)返回true,否則返回false表示授權(quán)失敗。

一般是使用Realm進(jìn)行授權(quán),此時(shí)繼承AuthorizingRealm,其流程是:
1.如果調(diào)用hasRole,則直接獲取AuthorizationInfo.getRoles()與傳入的角色比較即可;
2、如果調(diào)用isPermitted(“user:view”),首先通過PermissionResolver將權(quán)限字符串轉(zhuǎn)換成相應(yīng)的Permission實(shí)例,默認(rèn)使用WildcardPermissionResolver,即轉(zhuǎn)換為通配符的WildcardPermission;
3、通過AuthorizationInfo.getObjectPermissions()得到Permission實(shí)例集合;通過AuthorizationInfo. getStringPermissions()得到字符串集合并通過PermissionResolver解析為Permission實(shí)例;然后獲取用戶的角色,并通過RolePermissionResolver解析角色對(duì)應(yīng)的權(quán)限集合(默認(rèn)沒有實(shí)現(xiàn),可以自己提供);
4、接著調(diào)用Permission. implies(Permission p)逐個(gè)與傳入的權(quán)限比較,如果有匹配的則返回true,否則false。

ModularRealmAuthorizer進(jìn)行多Realm匹配流程:
1)首先檢查相應(yīng)的Realm是否實(shí)現(xiàn)了實(shí)現(xiàn)了Authorizer;
2)如果實(shí)現(xiàn)了Authorizer,那么接著調(diào)用其相應(yīng)的isPermitted/hasRole接口進(jìn)行匹配;
3)如果有一個(gè)Realm匹配那么將返回true,否則返回false。

SHIRO授權(quán)分析到此為止,祝工作順利,天天開心!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容