Apache Shiro學習筆記(三)用戶受權isPermitted過程

魯春利的工做筆記,好記性不如爛筆頭java



Shiro配置文件(shiro-authorize-permission.ini)數據庫

[main]
# 定義變量
# 變量名=全類名

[users]
# 用戶名=密碼,角色1,角色2,...,角色N
lucl=123,role1,role2
zs=123,role1

[roles]
# 角色=權限1,權限2,...,權限N
role1=user:create,user:update
role2=user:create,user:delete


單元測試apache

/**
 * 基於資源的訪問控制
 */
@Test
public void testWhetherHasPermission () {
    // 一、獲取SecurityManager工廠,此處使用Ini配置文件初始化SecurityManager
    Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro/authorize/shiro-authorize-permission.ini");
    
    // 二、獲得SecurityManager實例並綁定給SecurityUtils
    org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
    SecurityUtils.setSecurityManager(securityManager);
    
    // 三、獲得Subject及建立用戶名/密碼身份驗證Token(即用戶身份/憑證)
    Subject subject = SecurityUtils.getSubject();

    UsernamePasswordToken token = new UsernamePasswordToken("lucl", "123");
    try{
        // 四、登陸,即身份驗證
        subject.login(token);
    } catch (AuthenticationException e) {
        // 五、身份驗證失敗
        logger.info("用戶身份驗證失敗");
        e.printStackTrace();
    }
    
    // 用戶身份獲得確認
    if (subject.isAuthenticated()) {
        logger.info("用戶登陸成功。");
        /**
         * 進行權限判斷
         */
        // 判斷擁有權限:user:create
        Assert.assertTrue(subject.isPermitted("user:create"));
        // 判斷擁有權限:user:update and user:delete
        Assert.assertTrue(subject.isPermittedAll("user:update", "user:delete"));
        // 判斷沒有權限:user:view
        Assert.assertFalse(subject.isPermitted("user:view"));
        
        // 斷言擁有權限:user:create
        subject.checkPermission("user:create");
        // 斷言擁有權限:user:delete and user:update
        subject.checkPermissions("user:delete", "user:update");
        // 斷言擁有權限:user:view 失敗拋出異常
        subject.checkPermissions("user:view");
    } else {
        logger.info("用戶登陸失敗。");
    }

    // 六、退出
    subject.logout();
}


isPermitted過程緩存

  • subject.isPermittedapp

package org.apache.shiro.subject.support;

public class DelegatingSubject implements Subject {
    /** hasPrincipals()判斷身份信息,身份信息是在login方法登陸後賦值的 */
    public boolean isPermitted(String permission) {
        return hasPrincipals() && securityManager.isPermitted(getPrincipals(), permission);
    }
    
    public void login(AuthenticationToken token) throws AuthenticationException {
        clearRunAsIdentitiesInternal();
        /** 身份認證 */
        Subject subject = securityManager.login(this, token);

        PrincipalCollection principals;

        String host = null;

        if (subject instanceof DelegatingSubject) {
            DelegatingSubject delegating = (DelegatingSubject) subject;
            //we have to do this in case there are assumed identities - we don't want to lose the 'real' principals:
            principals = delegating.principals;
            host = delegating.host;
        } else {
            principals = subject.getPrincipals();
        }

        if (principals == null || principals.isEmpty()) {
            // 異常信息
            throw new IllegalStateException(msg);
        }
        this.principals = principals;
        this.authenticated = true;
        // 代碼略
    }
}


  • securityManager.isPermittedide

securityManager的實現爲DefaultSecurityManager,但DefaultSecurityManager無isPermitted方法。
單元測試

org.apache.shiro.mgt.DefaultSecurityManager
    extends org.apache.shiro.mgt.SessionsSecurityManager
        extends org.apache.shiro.mgt.AuthorizingSecurityManager
            extends org.apache.shiro.mgt.AuthenticatingSecurityManager


  • 調用AuthorizingSecurityManager的isPermitted方法
    測試

package org.apache.shiro.mgt;

public abstract class AuthorizingSecurityManager extends AuthenticatingSecurityManager {
    /**
     * The wrapped instance to which all of this SecurityManager authorization calls are delegated.
     * 受權的核心接口
     */
    private Authorizer authorizer;
    
    public AuthorizingSecurityManager() {
        super();
        this.authorizer = new ModularRealmAuthorizer();
    }
    
    public boolean isPermitted(PrincipalCollection principals, Permission permission) {
        return this.authorizer.isPermitted(principals, permission);
    }
    
    // 其餘代碼略
}


  • ModularRealmAuthorizer.isPermitted方法
    this

package org.apache.shiro.authz;

public class ModularRealmAuthorizer implements Authorizer, PermissionResolverAware, RolePermissionResolverAware {

    /**
     * The realms to consult during any authorization check.
     */
    protected Collection<Realm> realms;
    
    protected PermissionResolver permissionResolver;
    
    protected RolePermissionResolver rolePermissionResolver;
    
    public ModularRealmAuthorizer() {
        // 無參構造方法
    }
    
    public ModularRealmAuthorizer(Collection<Realm> realms) {
        setRealms(realms);
    }
    
    /* 權限判斷 */
    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;
    }

}

轉了一圈,具體的isPermitted仍是經過最終的realm來完成(IniRealm)
spa

org.apache.shiro.realm.text.IniRealm
    extends org.apache.shiro.realm.text.TextConfigurationRealm
        extends org.apache.shiro.realm.SimpleAccountRealm
            extends org.apache.shiro.realm.AuthorizingRealm


  • AuthorizingRealm.isPermitted被調用

package org.apache.shiro.realm;

 /**
  * An AuthorizingRealm extends the AuthenticatingRealm's capabilities by adding Authorization (access control) support. 
  * @see org.apache.shiro.authz.SimpleAuthorizationInfo
  * @since 0.2
  */
public abstract class AuthorizingRealm extends AuthenticatingRealm
        implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware {

    private PermissionResolver permissionResolver;

    private RolePermissionResolver permissionRoleResolver;
    
    public AuthorizingRealm() {
        this(null, null);
    }
    
    
    public AuthorizingRealm(CacheManager cacheManager) {
        this(cacheManager, null);
    }

    public AuthorizingRealm(CredentialsMatcher matcher) {
        this(null, matcher);
    }

    public AuthorizingRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
        /** super()會調用父類的無參構造方法,也就是new AuthenticatingRealm() {this(null, new SimpleCredentialsMatcher());} */
        super();
        if (cacheManager != null) setCacheManager(cacheManager);
        if (matcher != null) setCredentialsMatcher(matcher);

        this.authorizationCachingEnabled = true;
        // permissionResolver的實現類,自定義Permission時自定了也自定義了這個東西
        this.permissionResolver = new WildcardPermissionResolver();

        // 代碼略
    }
    
    public boolean isPermitted(PrincipalCollection principals, String permission) {
        // WildcardPermissionResolver.resolvePermission {return new WildcardPermission(permissionString);}
        Permission p = getPermissionResolver().resolvePermission(permission);
        return isPermitted(principals, p);
    }

    public boolean isPermitted(PrincipalCollection principals, Permission permission) {
        /** 
         * getAuthorizationInfo(principals)實際返回的是SimpleAccount;
         * 若是咱們在自定義的Realm中實現了info.add("system:edit:1"),那麼SimpleAccount就獲取到了其擁有的權限列表:
         * 說明:也就是shiro不維護權限信息,其應該具備的權限信息是由業務系統根據實際狀況來設定的
         * isPermitted會比較傳入的權限字符串,是否在實際設定的權限列表中(權限列表通常根據登陸用戶權限從數據庫中讀取並加載)
         */
        AuthorizationInfo info = getAuthorizationInfo(principals);
        return isPermitted(permission, info);
    }
    
    /** 最終的判斷方法(權限集合是在Realm實現時添加的權限列表,如info.add("system:edit:1")) */
    protected boolean isPermitted(Permission permission, AuthorizationInfo info) {
        Collection<Permission> perms = getPermissions(info);
        if (perms != null && !perms.isEmpty()) {
            for (Permission perm : perms) {
                /** Permission的implies方法總算是被調用到了 */
                if (perm.implies(permission)) {
                    return true;
                }
            }
        }
        return false;
    }
    
    protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {

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

        AuthorizationInfo info = null;

        // 代碼略,主要實現從緩存中獲取受權數據

        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.
            // 代碼略,獲取後從新加入緩存中
        }

        return info;
    }
    
    // 該類的doGetAuthorizationInfo方法爲抽象方法,須要子類根據身份信息實現本身的受權
    protected abstract AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals);
}


  • SimpleAccountRealm.doGetAuthorizationInfo方法

package org.apache.shiro.realm;

public class SimpleAccountRealm extends AuthorizingRealm {

    // 部分代碼略
    
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = getUsername(principals);
        USERS_LOCK.readLock().lock();
        try {
            return this.users.get(username);    // 返回SimpleAccount
        } finally {
            USERS_LOCK.readLock().unlock();
        }
    }
}
相關文章
相關標籤/搜索