Shiro Realm设计概念

Realm

shiro最干实事的,其行为有

CachingRealm

其行为主要是注入缓存管理器、控制缓存开关、提供关于缓存的公共行为,他不提供缓存具体的认证和授权信息,该部分由其子类具体缓存

AuthenticatingRealm

具备缓存从数据库查出的用户信息(Cache<Object, AuthenticationInfo>),其行为主要是执行认证的具备行为,其具备CredentialsMatcher凭证匹配器,主要用于匹配用户输入的Token和数据库查出的Info是否匹配。该部分的用户信息还会注入到Subject中,为校验用户权限使用,但是就有了一个问题,用户的权限更新后怎么办,Shiro使用了runAs方式。 

用户的权限信息更新后需要更新Subject中获取PrincipalCollection的方式,即runAs()方式,还需更新认证、授权缓存信息

package com.wjz.demo;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;

public class CustomAuthorizingRealm extends AuthorizingRealm {

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        return new SimpleAuthenticationInfo("wjz", "123", getName());
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.addRole("buyGoods");
        return authorizationInfo;
    }

    /**
     * 用户的权限信息被更新后须执行该方法
     * 
     * @param latestPrincipal
     *            最新的用户权限信息
     */
    public void reloadAuthorizing(Object latestPrincipal) {
        Subject subject = SecurityUtils.getSubject();
        PrincipalCollection expiredPrincipalCollection = subject.getPrincipals();
        String realmName = getName();
        final SimplePrincipalCollection latestPrincipalCollection = new SimplePrincipalCollection(latestPrincipal,
                realmName);
        subject.releaseRunAs();
        subject.runAs(latestPrincipalCollection);

        // 如果认证、授权还有缓存功能的话执行以下内容
        if (isCachingEnabled()) {
            clearCache(expiredPrincipalCollection);

            CacheManager cacheManager = getCacheManager();
            if (isAuthenticationCachingEnabled()) {
                Cache<Object, AuthenticationInfo> authenticationCache = cacheManager
                        .getCache(getAuthenticationCacheName());
                setAuthenticationCache(authenticationCache);
            }
            if (isAuthorizationCachingEnabled()) {
                Cache<Object, AuthorizationInfo> authorizationCache = cacheManager
                        .getCache(getAuthorizationCacheName());
                setAuthorizationCache(authorizationCache);
            }
        }
    }
}

核心逻辑方法

public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

    // 试图从缓存中获得,没有缓存时使用CacheManager创建
    AuthenticationInfo info = getCachedAuthenticationInfo(token);
    if (info == null) {
        // 子类具体实现返回具有从数据库查出的用户信息的AuthenticationInfo
        info = doGetAuthenticationInfo(token);
        log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
        if (token != null && info != null) {
            // 开启了缓存的话将AuthenticationInfo放到缓存中,Token的principal为key(UsernamePasswordToken的principal即username)
            cacheAuthenticationInfoIfPossible(token, info);
        }
    } else {
        log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
    }

    if (info != null) {
        // Token和Info进行匹配
        assertCredentialsMatch(token, info);
    } else {
        log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
    }

    return info;
}

AuthorizingRealm

具备了权限解析器(PermissionResolver)和从数据库查出的用户权限缓存(Cache<Object, AuthorizationInfo>),其行为主要是校验用户的权限

核心逻辑方法根据Subject中的PrincipalCollection获得会员的权限信息

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) {
        // 子类具体实现返回具有从数据库查出的用户权限信息的AuthorizationInfo
        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 + "].");
            }
            // 开启了缓存的话将AuthorizationInfo放到缓存中,PrincipalCollection为key
            Object key = getAuthorizationCacheKey(principals);
            cache.put(key, info);
        }
    }

    return info;
}

校验前期准备

public boolean isPermitted(PrincipalCollection principals, String permission) {
    // 使用权限解析器解析字符串获得用户的权限信息,实例化 WildcardPermission 并注入权限字符串
    Permission p = getPermissionResolver().resolvePermission(permission);
    return isPermitted(principals, p);
}

public boolean isPermitted(PrincipalCollection principals, Permission permission) {
    // 调用核心逻辑方法获得用户的权限信息
    AuthorizationInfo info = getAuthorizationInfo(principals);
    return isPermitted(permission, info);
}

具体的校验

protected Collection<Permission> getPermissions(AuthorizationInfo info) {
    Set<Permission> permissions = new HashSet<Permission>();

    // 当子类查询数据库查出用户权限信息后拼装AuthorizationInfo时可以注入字符串权限
    // 也可以注入对象权限即 WildcardPermission 
    if (info != null) {
        Collection<Permission> perms = info.getObjectPermissions();
        if (!CollectionUtils.isEmpty(perms)) {
            permissions.addAll(perms);
        }
        // 是字符串权限的话还得解析为对象权限 WildcardPermission
        perms = resolvePermissions(info.getStringPermissions());
        if (!CollectionUtils.isEmpty(perms)) {
            permissions.addAll(perms);
        }

        perms = resolveRolePermissions(info.getRoles());
        if (!CollectionUtils.isEmpty(perms)) {
            permissions.addAll(perms);
        }
    }

    if (permissions.isEmpty()) {
        return Collections.emptySet();
    } else {
        return Collections.unmodifiableSet(permissions);
    }
}

//visibility changed from private to protected per SHIRO-332
protected boolean isPermitted(Permission permission, AuthorizationInfo info) {
    Collection<Permission> perms = getPermissions(info);
    if (perms != null && !perms.isEmpty()) {
        for (Permission perm : perms) {
            // 权限匹配
            if (perm.implies(permission)) {
                return true;
            }
        }
    }
    return false;
}

 

 

posted @ 2018-08-23 18:30  BINGJJFLY  阅读(543)  评论(0编辑  收藏  举报