文档章节

shiro源码分析(三)授权、认证、缓存的接口设计

乒乓狂魔
 乒乓狂魔
发布于 2015/02/07 10:19
字数 3604
阅读 291
收藏 2
前两篇文章主要说的是认证过程,这一篇来分析下授权的过程。还是开涛大神的案例(http://jinnianshilongnian.iteye.com/blog/2020017),如下:
public class ShiroTest {

	@Test  
	public void testHelloworld() {  
		init();
		Subject subject=login("zhang","123");
		Assert.assertTrue(subject.hasRole("role1"));
		Assert.assertTrue(subject.hasRole("role2"));
		Assert.assertTrue(subject.hasRole("role3"));
	}
	
	private Subject login(String userName,String password){
		 //3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)  
	    Subject subject = SecurityUtils.getSubject();  
	    UsernamePasswordToken token = new UsernamePasswordToken(userName,password);  
	    subject.login(token);
	    return subject;
	}
	
	private void init(){
		 //1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager  
	    Factory<org.apache.shiro.mgt.SecurityManager> factory =  
	            new IniSecurityManagerFactory("classpath:shiro.ini");  
	    //2、得到SecurityManager实例 并绑定给SecurityUtils  
	    org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();  
	    SecurityUtils.setSecurityManager(securityManager);  
	}
}

ini配置文件如下:
[users]
zhang=123,role1,role2  
wang=123,role1

从subject.hasRole开始入手,默认的Subject为DelegatingSubject:
public boolean hasRole(String roleIdentifier) {
        return hasPrincipals() && securityManager.hasRole(getPrincipals(), roleIdentifier);
    }

首先就是该用户是否已登录,验证角色的地方在securityManager的hasRole方法中:
public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
        return this.authorizer.hasRole(principals, roleIdentifier);
    }

AuthorizingSecurityManager实现了Authorizer接口,但是AuthorizingSecurityManager是通过内部Authorizer引用来完成具体的功能,默认采用的是ModularRealmAuthorizer。如下:
public abstract class AuthorizingSecurityManager extends AuthenticatingSecurityManager {

    /**
     * The wrapped instance to which all of this <tt>SecurityManager</tt> authorization calls are delegated.
     */
    private Authorizer authorizer;

    public AuthorizingSecurityManager() {
        super();
        this.authorizer = new ModularRealmAuthorizer();
    }
//略
}

来看看这个Authorizer模块的接口设计:
public interface Authorizer {
    boolean isPermitted(PrincipalCollection principals, String permission);
    boolean isPermitted(PrincipalCollection subjectPrincipal, Permission permission);
    boolean[] isPermitted(PrincipalCollection subjectPrincipal, String... permissions);
    boolean[] isPermitted(PrincipalCollection subjectPrincipal, List<Permission> permissions);
    boolean isPermittedAll(PrincipalCollection subjectPrincipal, String... permissions);
    boolean isPermittedAll(PrincipalCollection subjectPrincipal, Collection<Permission> permissions);

    void checkPermission(PrincipalCollection subjectPrincipal, String permission) throws AuthorizationException;
    void checkPermission(PrincipalCollection subjectPrincipal, Permission permission) throws AuthorizationException;
    void checkPermissions(PrincipalCollection subjectPrincipal, String... permissions) throws AuthorizationException;
    void checkPermissions(PrincipalCollection subjectPrincipal, Collection<Permission> permissions) throws AuthorizationException;

    boolean hasRole(PrincipalCollection subjectPrincipal, String roleIdentifier);
    boolean[] hasRoles(PrincipalCollection subjectPrincipal, List<String> roleIdentifiers);
    boolean hasAllRoles(PrincipalCollection subjectPrincipal, Collection<String> roleIdentifiers);

    void checkRole(PrincipalCollection subjectPrincipal, String roleIdentifier) throws AuthorizationException;
    void checkRoles(PrincipalCollection subjectPrincipal, Collection<String> roleIdentifiers) throws AuthorizationException;
    void checkRoles(PrincipalCollection subjectPrincipal, String... roleIdentifiers) throws AuthorizationException;
    
}

从上面的接口中,可以分成两大类,第一类是验证用户的某个或某些权限,第二类是验证用户的某个角色或某些角色。角色则是一组权限的集合,所以后者是粗粒度的验证,而前者是细粒度的验证。对于那些check方法则是验证不通过时抛出异常。
接口实现类图为:


可以看到很多的Realm都实现了该接口,即这些Realm不仅提供登陆验证,还提供权限验证。
先来看下默认使用的ModularRealmAuthorizer:

public class ModularRealmAuthorizer implements Authorizer, PermissionResolverAware, RolePermissionResolverAware {
    protected Collection<Realm> realms;

    protected PermissionResolver permissionResolver;

    protected RolePermissionResolver rolePermissionResolver;
    //略
}

可以看到,它有三个重要属性,Realm集合和PermissionResolver 、RolePermissionResolver 。PermissionResolver 是什么呢?
public interface PermissionResolver {
    Permission resolvePermission(String permissionString);
}

就是将权限字符串解析成Permission 对象,同理RolePermissionResolver 如下:
public interface RolePermissionResolver {
    Collection<Permission> resolvePermissionsInRole(String roleString);
}

将角色字符串解析成Permission 集合。
来看下这几个方法:

public ModularRealmAuthorizer(Collection<Realm> realms) {
        setRealms(realms);
    }
public void setRealms(Collection<Realm> realms) {
        this.realms = realms;
        applyPermissionResolverToRealms();
        applyRolePermissionResolverToRealms();
    }
public void setPermissionResolver(PermissionResolver permissionResolver) {
        this.permissionResolver = permissionResolver;
        applyPermissionResolverToRealms();
    }
public void setRolePermissionResolver(RolePermissionResolver rolePermissionResolver) {
        this.rolePermissionResolver = rolePermissionResolver;
        applyRolePermissionResolverToRealms();
    }
protected void applyRolePermissionResolverToRealms() {
        RolePermissionResolver resolver = getRolePermissionResolver();
        Collection<Realm> realms = getRealms();
        if (resolver != null && realms != null && !realms.isEmpty()) {
            for (Realm realm : realms) {
                if (realm instanceof RolePermissionResolverAware) {
                    ((RolePermissionResolverAware) realm).setRolePermissionResolver(resolver);
                }
            }
        }
    }
protected void applyPermissionResolverToRealms() {
        PermissionResolver resolver = getPermissionResolver();
        Collection<Realm> realms = getRealms();
        if (resolver != null && realms != null && !realms.isEmpty()) {
            for (Realm realm : realms) {
                if (realm instanceof PermissionResolverAware) {
                    ((PermissionResolverAware) realm).setPermissionResolver(resolver);
                }
            }
        }
    }

看下这几个set方法,其目的都是如果哪些Realm 想要PermissionResolver 或RolePermissionResolver 参数,则将ModularRealmAuthorizer 的对应参数传给它。
再来看ModularRealmAuthorizer 是如何实现Authorizer接口的:

protected void assertRealmsConfigured() throws IllegalStateException {
        Collection<Realm> realms = getRealms();
        if (realms == null || realms.isEmpty()) {
            String msg = "Configuration error:  No realms have been configured!  One or more realms must be " +
                    "present to execute an authorization operation.";
            throw new IllegalStateException(msg);
        }
    }
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;
    }

首先是判断Collection<Realm> realms集合是否为空,然后就是将那些实现了Authorizer接口的Realm 来判断是否具有某个权限,也就是ModularRealmAuthorizer本身并不去权限验证,而是交给那些具有权限验证功能的Realm去验证(即那些Realm实现了Authorizer接口)。所以
ModularRealmAuthorizer并不具有太多实际内容,我们转战那些实现了Authorizer接口的Realm,去看看他们的验证过程。
这时候,就需要看Authorizer接口的另一个分支即下图AuthorizingRealm分支:


AuthorizingRealm涉及到Realm,所以再把Realm说清楚。Realm接口如下:

public interface Realm {
    String getName();
    boolean supports(AuthenticationToken token);
    AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;
}

Realm 本身只具有验证用户是否合法的功能,不具有授权的功能。再看它的实现者CachingRealm,从名字上就可以知道加入了缓存功能:
private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger();

    private String name;
    private boolean cachingEnabled;
    private CacheManager cacheManager;
public CachingRealm() {
        this.cachingEnabled = true;
        this.name = getClass().getName() + "_" + INSTANCE_COUNT.getAndIncrement();
    }

有3个对象属性和一个类属性,INSTANCE_COUNT 主要是用来计数Realm的个数的,同时追加到name属性中,cachingEnabled对外提供get、set方法,这里的cachingEnabled就相当于一个总开关,它的子类都有子开关,共同决定着是否进行缓存,如它的子类AuthenticatingRealm:
private boolean authenticationCachingEnabled;
public boolean isAuthenticationCachingEnabled() {
        return this.authenticationCachingEnabled && isCachingEnabled();
    }
 public void setAuthenticationCachingEnabled(boolean authenticationCachingEnabled) {
        this.authenticationCachingEnabled = authenticationCachingEnabled;
        if (authenticationCachingEnabled) {
            setCachingEnabled(true);
        }
    }

从这里就可以看到两个cacheEnabled的作用。也对外提供CacheManager的get、set方法, CachingRealm本身并没有做太多内容,就是把这几个参数收集起来,供子类去使用。
接下来看下Cache缓存的整体结构图:

我们要先看下CacheManager是干嘛的:

public interface CacheManager {
    public <K, V> Cache<K, V> getCache(String name) throws CacheException;
}

根据name获取一个Cache<K, V>这样的结构,看起来像HashMap的结构,这里的name到底是什么呢?
CachingRealm的子类AuthenticatingRealm有一个authenticationCacheName属性,而这里的authenticationCacheName就是我们刚才要找的目标,证据如下:

private Cache<Object, AuthenticationInfo> getAuthenticationCacheLazy() {

        if (this.authenticationCache == null) {

            log.trace("No authenticationCache instance set.  Checking for a cacheManager...");

            CacheManager cacheManager = getCacheManager();

            if (cacheManager != null) {
      //这里的getAuthenticationCacheName()就是获取authenticationCacheName
                String cacheName = getAuthenticationCacheName();
                log.debug("CacheManager [{}] configured.  Building authentication cache '{}'", cacheManager, cacheName);
                this.authenticationCache = cacheManager.getCache(cacheName);
            }
        }

        return this.authenticationCache;
    }

再看下authenticationCacheName的构成:
public AuthenticatingRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
        authenticationTokenClass = UsernamePasswordToken.class;

        //retain backwards compatibility for Shiro 1.1 and earlier.  Setting to true by default will probably cause
        //unexpected results for existing applications:
        this.authenticationCachingEnabled = false;

        int instanceNumber = INSTANCE_COUNT.getAndIncrement();
        this.authenticationCacheName = getClass().getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
        if (instanceNumber > 0) {
            this.authenticationCacheName = this.authenticationCacheName + "." + instanceNumber;
        }

        if (cacheManager != null) {
            setCacheManager(cacheManager);
        }
        if (matcher != null) {
            setCredentialsMatcher(matcher);
        }
    }

在创建AuthenticatingRealm时,authenticationCacheName 默认是当前类名+DEFAULT_AUTHORIZATION_CACHE_SUFFIX(为.authenticationCache)+数量。这个数量也是用来统计AuthenticatingRealm的个数的,这种方式仅仅是默认的,也可以去修改:
public void setAuthenticationCacheName(String authenticationCacheName) {
        this.authenticationCacheName = authenticationCacheName;
    }
public void setName(String name) {
        super.setName(name);
        String authcCacheName = this.authenticationCacheName;
        if (authcCacheName != null && authcCacheName.startsWith(getClass().getName())) {
            //get rid of the default heuristically-created cache name.  Create a more meaningful one
            //based on the application-unique Realm name:
            this.authenticationCacheName = name + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
        }
    }

这两种方式都可以去修改。回到CacheManager:
public interface CacheManager {
    public <K, V> Cache<K, V> getCache(String name) throws CacheException;
}

然后就需要了解下Cache<K, V>这个结构:
public interface Cache<K, V> {
    public V get(K key) throws CacheException;
    public V put(K key, V value) throws CacheException;
    public V remove(K key) throws CacheException;
    public void clear() throws CacheException;
    public int size();
    public Set<K> keys();
    public Collection<V> values();
}

这基本上不就是map的结构吗?为什么还要单独设计这样的结构呢?来看下它的文档介绍就知道了:
/**
 * A Cache efficiently stores temporary objects primarily to improve an application's performance.
 *
 * <p>Shiro doesn't implement a full Cache mechanism itself, since that is outside the core competency of a
 * Security framework.  Instead, this interface provides an abstraction (wrapper) API on top of an underlying
 * cache framework's cache instance (e.g. JCache, Ehcache, JCS, OSCache, JBossCache, TerraCotta, Coherence,
 * GigaSpaces, etc, etc), allowing a Shiro user to configure any cache mechanism they choose.
 *
 * @since 0.2
 */

Shiro并不打算自己实现一个完整的缓存机制,因为这并不是安全框架的主要职责,相反它应该提供一个统一的API接口,可以加入不同缓存框架。而对于我们用户来说,只需针对这一层统一API进行编程,不再针对某个具体的缓存框架编程,这样就更加容易切换不同的缓存框架。
再看下,它的实现类MapCache和EhCache,MapCache很简单就是通过Map结构来实现

public class MapCache<K, V> implements Cache<K, V> {
    private final Map<K, V> map;
    private final String name;

    public MapCache(String name, Map<K, V> backingMap) {
        if (name == null) {
            throw new IllegalArgumentException("Cache name cannot be null.");
        }
        if (backingMap == null) {
            throw new IllegalArgumentException("Backing map cannot be null.");
        }
        this.name = name;
        this.map = backingMap;
    }

    public V get(K key) throws CacheException {
        return map.get(key);
    }

    public V put(K key, V value) throws CacheException {
        return map.put(key, value);
    }

    public V remove(K key) throws CacheException {
        return map.remove(key);
    }
    //略
}

EhCache则是通过net.sf.ehcache.Ehcache框架来来实现,不再涉及。
Cache<K, V>知道了,又有哪些CacheManager的实现呢?
AbstractCacheManager如下:

public abstract class AbstractCacheManager implements CacheManager, Destroyable {
    private final ConcurrentMap<String, Cache> caches;
    public AbstractCacheManager() {
        this.caches = new ConcurrentHashMap<String, Cache>();
    }

    public <K, V> Cache<K, V> getCache(String name) throws IllegalArgumentException, CacheException {
        if (!StringUtils.hasText(name)) {
            throw new IllegalArgumentException("Cache name cannot be null or empty.");
        }
        Cache cache;
        cache = caches.get(name);
        if (cache == null) {
            cache = createCache(name);
            Cache existing = caches.putIfAbsent(name, cache);
            if (existing != null) {
                cache = existing;
            }
        }
        return cache;
    }
    //略
}

也很简单,内部拥有一个ConcurrentHashMap集合,存取都是对该集合的操作,而把真正创建Cache的操作留给具体的子类来实现,即createCache方法。看下它的子类MemoryConstrainedCacheManager的createCache实现:
public class MemoryConstrainedCacheManager extends AbstractCacheManager {
    @Override
    protected Cache createCache(String name) {
        return new MapCache<Object, Object>(name, new SoftHashMap<Object, Object>());
    }
}

就是创建了一个MapCache对象作为Cache,至于SoftHashMap则需要单独去介绍其中的设计。
CachingRealm就大致介绍完了,回到它的子类,看它的子类AuthenticatingRealm是怎么去使用CacheManager。该子类主要完成认证流程,首先是其的初始化,AuthenticatingRealm及其子类都实现了Initializable接口,初始化的时候会首先获取其缓存,如下:

public final void init() {
        //trigger obtaining the authorization cache if possible
        getAvailableAuthenticationCache();
        onInit();
    }
private Cache<Object, AuthenticationInfo> getAvailableAuthenticationCache() {
        Cache<Object, AuthenticationInfo> cache = getAuthenticationCache();
        boolean authcCachingEnabled = isAuthenticationCachingEnabled();
        if (cache == null && authcCachingEnabled) {
            cache = getAuthenticationCacheLazy();
        }
        return cache;
    }
private Cache<Object, AuthenticationInfo> getAuthenticationCacheLazy() {

        if (this.authenticationCache == null) {

            log.trace("No authenticationCache instance set.  Checking for a cacheManager...");

            CacheManager cacheManager = getCacheManager();

            if (cacheManager != null) {
                String cacheName = getAuthenticationCacheName();
                log.debug("CacheManager [{}] configured.  Building authentication cache '{}'", cacheManager, cacheName);
                this.authenticationCache = cacheManager.getCache(cacheName);
            }
        }

        return this.authenticationCache;
    }

首先会获取Cache<Object, AuthenticationInfo> cache属性,如果没有,再判断是否允许缓存,如果允许,则通过CacheManager 来获取,之前已分析过,如果还没有则会创建一个Cache,然后返回。
再看下认证过程如下:

public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        AuthenticationInfo info = getCachedAuthenticationInfo(token);
        if (info == null) {
            //otherwise not cached, perform the lookup:
            info = doGetAuthenticationInfo(token);
            log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
            if (token != null && info != null) {
                cacheAuthenticationInfoIfPossible(token, info);
            }
        } else {
            log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
        }

        if (info != null) {
            assertCredentialsMatch(token, info);
        } else {
            log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
        }

        return info;
    }

首先从缓存中尝试是否能找到AuthenticationInfo ,如果找不到,则需要子类去完成具体的认证细节,然后再存储到缓存中,因为本类并没有具体的数据源,只有缓存源,所以本类只是搭建了认证流程,具体的认证细节则由具体的子类来完成,所以 doGetAuthenticationInfo(token)是一个protected的抽象方法,如下:
protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;

当缓存中存在或者子类进行具体的认证后,下一步的操作是要进行密码匹配的过程,AuthenticatingRealm有一个属性CredentialsMatcher credentialsMatcher,接口如下:
public interface CredentialsMatcher {
    boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info);
}

就是匹配我们认证时的AuthenticationToken 和刚才已找到的AuthenticationInfo 是否匹配。有如下的实现类:AllowAllCredentialsMatcher、PasswordMatcher、SimpleCredentialsMatcher等等。AuthenticatingRealm的构造函数默认使用的是SimpleCredentialsMatcher:
public AuthenticatingRealm() {
        this(null, new SimpleCredentialsMatcher());
    }

    public AuthenticatingRealm(CacheManager cacheManager) {
        this(cacheManager, new SimpleCredentialsMatcher());
    }

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

这一块内容先暂时不讲,后续文章再来详细说明。
当你匹配通过了,则就算认证成功了。认证流程就在AuthenticatingRealm中完成了。
我们再向它的子类AuthorizingRealm研究,这个就有涉及到授权的功能了。AuthenticatingRealm是将整个认证流程框架化,AuthorizingRealm则是将整个授权流程框架化,AuthorizingRealm也有授权缓存,所以会通过父类CachingRealm来获取CacheManager,同时也有一个子缓存开关authorizationCachingEnabled,和AuthenticatingRealm基本类似,属性如下:

public abstract class AuthorizingRealm extends AuthenticatingRealm
        implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware {

    private static final String DEFAULT_AUTHORIZATION_CACHE_SUFFIX = ".authorizationCache";

    private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger();

    private boolean authorizationCachingEnabled;
    private Cache<Object, AuthorizationInfo> authorizationCache;
    private String authorizationCacheName;

    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();
        if (cacheManager != null) setCacheManager(cacheManager);
        if (matcher != null) setCredentialsMatcher(matcher);

        this.authorizationCachingEnabled = true;
        this.permissionResolver = new WildcardPermissionResolver();

        int instanceNumber = INSTANCE_COUNT.getAndIncrement();
        this.authorizationCacheName = getClass().getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
        if (instanceNumber > 0) {
            this.authorizationCacheName = this.authorizationCacheName + "." + instanceNumber;
        }
    }
//略
}

AtomicInteger 同样是用于对那些具有授权功能的Realm进行数量统计的,authorizationCachingEnabled缓存子开关,authorizationCache缓存,authorizationCacheName缓存名字。 PermissionResolver permissionResolver、RolePermissionResolver permissionRoleResolver这两个则是对字符串进行解析对应的Permission和Collection<Permission>的。我们来看下AuthorizingRealm的主要功能,对于授权接口Authorizer的实现:
public boolean hasRole(PrincipalCollection principal, String roleIdentifier) {
        AuthorizationInfo info = getAuthorizationInfo(principal);
        return hasRole(roleIdentifier, info);
    }

首先就是获取授权信息,看下getAuthorizationInfo:
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;
    }

同样很容易理解,先得到缓存,从缓存中去找有没有授权信息,如果没有,则需要子类去完成具体的授权细节即doGetAuthorizationInfo,授权完成后放置缓存中。同样doGetAuthorizationInfo是protected的抽象方法,由子类去实现。PermissionResolver permissionResolver、RolePermissionResolver permissionRoleResolver则是发挥如下作用:
private Collection<Permission> getPermissions(AuthorizationInfo info) {
        Set<Permission> permissions = new HashSet<Permission>();

        if (info != null) {
            Collection<Permission> perms = info.getObjectPermissions();
            if (!CollectionUtils.isEmpty(perms)) {
                permissions.addAll(perms);
            }
            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);
        }
    }

即有了授权信息AuthorizationInfo 后,获取所有的权限Permission,有三种途径来收集,第一种就是info.getObjectPermissions() info中直接含有Permission对象集合,第二种就是info.getStringPermissions() info中有字符串形式的权限表示,第三种就是info.getRoles() info中含有角色集合,角色也是一组权限的集合,看下resolvePermissions(info.getStringPermissions()):
private Collection<Permission> resolvePermissions(Collection<String> stringPerms) {
        Collection<Permission> perms = Collections.emptySet();
        PermissionResolver resolver = getPermissionResolver();
        if (resolver != null && !CollectionUtils.isEmpty(stringPerms)) {
            perms = new LinkedHashSet<Permission>(stringPerms.size());
            for (String strPermission : stringPerms) {
                Permission permission = getPermissionResolver().resolvePermission(strPermission);
                perms.add(permission);
            }
        }
        return perms;
    }

也很简单,对于每一个strPermission 通过PermissionResolver 转化成Permission 对象,对于resolveRolePermissions也同理,不再说明。这里具体的转化细节先暂且不说,后续再将。
现在终于把认证流程和授权框架流程大致说完了,即AuthenticatingRealm和AuthorizingRealm的内容,他们分别留给子类protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;具体的认证方法和protected abstract AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)具体的授权方法。


作者:乒乓狂魔

© 著作权归作者所有

共有 人打赏支持
乒乓狂魔
粉丝 996
博文 105
码字总数 271356
作品 0
长宁
程序员
Shiro源码分析-初始化-Realm

在上一篇介绍SecurityManager的初始化过程中,也有realm的粗略介绍。 realm的概念在安全领域随处可见: 各种中间件的realm、spring security的realm、shiro的realm。。。如下: tomcat的rea...

Dead_knight
2014/04/08
0
0
Spring Security

重拾后端之Spring Boot(四):使用JWT和Spring Security保护REST API 重拾后端之Spring Boot(一):REST API的搭建可以这样简单重拾后端之Spring Boot(二):MongoDb的无缝集成重拾后端之...

掘金官方
01/04
0
0
跟我学Shiro

1.1 简介 Apache Shiro是Java的一个安全框架。目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不...

kevin728971010
2016/10/25
122
0
第一章 Shiro简介——《跟我学Shiro》

1.1 简介 Apache Shiro是Java的一个安全框架。目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不...

真爱2015
2015/12/15
87
0
Apache Shiro框架认识

1.1 简介 Apache Shiro是Java的一个安全框架。目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不...

tsmyk0715
2016/11/23
89
0

没有更多内容

加载失败,请刷新页面

加载更多

00.编译OpenJDK-8u40的整个过程

前言 历经2天的折腾总算把OpenJDK给编译成功了,要说为啥搞这个,还得从面试说起,最近出去面试经常被问到JVM的相关东西,总感觉自己以前学的太浅薄,所以回来就打算深入学习,目标把《深入理...

凌晨一点
39分钟前
0
0
python: 一些关于元组的碎碎念

初始化元组的时候,尤其是元组里面只有一个元素的时候,会出现一些很蛋疼的情况: def checkContentAndType(obj): print(obj) print(type(obj))if __name__=="__main__": tu...

Oh_really
昨天
2
2
jvm crash分析工具

介绍一款非常好用的jvm crash分析工具,当jvm挂掉时,会产生hs_err_pid.log。里面记录了jvm当时的运行状态以及错误信息,但是内容量比较庞大,不好分析。所以我们要借助工具来帮我们。 Cras...

xpbob
昨天
89
0
Qt编写自定义控件属性设计器

以前做.NET开发中,.NET直接就集成了属性设计器,VS不愧是宇宙第一IDE,你能够想到的都给你封装好了,用起来不要太爽!因为项目需要自从全面转Qt开发已经6年有余,在工业控制领域,有一些应用...

飞扬青云
昨天
3
0
我为什么用GO语言来做区块链?

Go语言现在常常被用来做去中心化系统(decentralised system)。其他类型的公司也都把Go用在产品的核心模块中,并且它在网站开发中也占据了一席之地。 我们在决定做Karachain的时候,考量(b...

HiBlock
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部