`

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

阅读更多
前两篇文章主要说的是认证过程,这一篇来分析下授权的过程。还是开涛大神的案例(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)具体的授权方法。


作者:乒乓狂魔
  • 大小: 9 KB
  • 大小: 14.8 KB
  • 大小: 8.3 KB
分享到:
评论

相关推荐

    shiro源码包

    - **源码分析**:通过阅读Shiro的源码,可以理解其内部的工作机制,包括如何进行身份验证、授权以及会话管理,有助于定制化开发和性能优化。 - **设计模式**:Shiro的源码中大量运用了设计模式,如工厂模式、代理...

    Shiro1.2.2_源码(压缩包)

    通过对 Shiro1.2.2 的源码分析,我们可以了解到它如何处理各种安全问题,以及如何构建高效、灵活的安全架构。同时,源码的学习有助于我们理解和解决在实际开发中遇到的安全问题,提升我们的编程技能。

    跟我学Shiro源码

    "跟我学Shiro源码"这个主题旨在深入理解Shiro的工作原理,通过分析源码来提升我们对安全编程的认知。 1. **Shiro架构** Shiro 的核心组件包括 Realm(认证与授权信息源)、SecurityManager(安全管理器)、Subject...

    shiro-root-1.2.3-source-release源码带注释.rar

    Shiro设计的核心理念是简洁,它将安全相关的功能划分为三个主要组件:认证(Authentication)、授权(Authorization)和会话管理(Session Management)。此外,它还提供加密服务来处理敏感数据。 2. **认证...

    Shiro入门到精通

    Apache Shiro 是一款轻量级的安全框架,专为Java应用设计,提供了认证、授权、会话管理和加密等核心安全功能。本课程“Shiro入门到精通”旨在帮助开发者全面理解和掌握Shiro的使用,通过与Spring Security的对比,...

    springboot+shiro开发的公司网站源码

    SpringBoot和Shiro是两个非常重要的...通过以上分析,我们可以看出这个源码项目涵盖了企业级Web开发的多个方面,包括前后端分离、数据库设计、安全控制等,对于学习和实践SpringBoot及Shiro的使用具有很高的参考价值。

    SpringBoot+Shiro权限管理系统脚手架.zip

    这个名为"SpringBoot+Shiro权限管理系统脚手架"的项目,为初学者提供了一个深入理解软件开发流程的绝佳实践平台,涵盖需求分析、系统设计、编码实现到测试部署的各个环节。 SpringBoot是基于Spring框架的简化版本,...

    springboot+mybatis+shiro的电商书城系统.zip

    Apache Shiro是一个强大且易用的Java安全框架,提供了认证、授权、加密和会话管理功能,可以非常容易地开发出足够安全的应用。在这个电商书城系统中,Shiro主要负责用户的身份验证(Authentication)、授权...

    基于SpringBoot设计的生鲜超市管理的设计与实现软件源码.zip

    本文将深入探讨一个基于SpringBoot框架设计的生鲜超市管理软件的实现,通过源码分析,帮助读者理解其设计思路和技术要点。 首先,SpringBoot是Java领域广泛使用的轻量级框架,它简化了Spring应用的初始搭建以及开发...

    springboot+shiro+jwt+vue全家桶+redis搭建的后台系统脚手架(前端部分).zip

    6. **源码分析**:压缩包中的“springboot_ym”可能是项目的源代码目录,包含了Java后端和Vue前端的代码。通过阅读源码,可以深入理解每个组件如何协同工作,如SpringBoot的配置、Shiro的安全逻辑、JWT的生成与验证...

    java商城源码

    这个源码通常包含了完整的前后端代码,以及数据库模型、接口设计、业务逻辑和用户体验等多个方面的实现。下面将详细介绍Java商城源码的相关知识点。 1. **Java技术栈**:Java商城源码的开发主要依赖Java技术栈,...

    基于SpringBoot的权限管理系统易读易懂、界面简洁美观 核心技术采用Spring、MyBatis、Shiro

    本文将深入探讨一个基于SpringBoot的权限管理系统,该系统易读易懂,界面设计简洁美观,核心技术栈包括Spring、MyBatis和Shiro。无需复杂的配置和额外的依赖,使得开发者可以快速上手并投入实际项目使用。 首先,...

    java大众点评讲师源码

    7. **安全机制**:源码中可能包含了用户认证和授权的实现,如使用Spring Security或Apache Shiro,确保只有合法用户能访问特定资源。 8. **缓存策略**:为了提高性能,源码可能会使用Redis或Memcached等缓存技术来...

    java的门户网站自动生成系统的设计与实现源码.zip

    8. **权限管理**:对于大型门户网站,权限控制是必要的,如Spring Security或Apache Shiro,可以实现用户认证和授权。 9. **缓存机制**:如Redis或Memcached,用于存储常用数据,提高网站响应速度,减轻服务器压力...

    Jspxcms 4.1源码包

    - **Shiro**:安全框架,处理用户认证、授权和会话管理,`ehcache-shiro.xml`涉及到Shiro的缓存配置。 - **IKAnalyzer**:中文分词器,用于提高全文检索和搜索的准确度,`IKAnalyzer.cfg.xml`是其配置文件。 3. *...

    springboot-shiros-mybatis-redis+mysql(前后分离)

    【Shiro】是Apache的一个开源安全框架,主要负责认证、授权、会话管理和并发控制等功能。在Spring Boot项目中,Shiro提供了一种轻量级的安全解决方案,用于保护应用程序的资源,例如API接口或页面。 【MyBatis】是...

    【java源码】CWBBS 2.4 论坛源码

    6. **安全机制**:为了保护用户数据和系统安全,源码可能包含了用户认证、授权、防止SQL注入、XSS攻击的防护措施,比如使用Spring Security或Apache Shiro。 7. **文件上传与下载**:论坛系统可能包含文件上传功能...

    JSP基于SpringMVC和Hibernate生离校就业分析管理平台源码案例设计.zip

    《基于JSP、SpringMVC和Hibernate的生离校就业分析管理平台源码解析》 在信息化时代,教育管理系统的建设对于高校来说至关重要,而基于JSP、SpringMVC和Hibernate构建的生离校就业分析管理平台正是这样一个高效、...

    教职工教学管理毕业设计源码

    教职工教学管理毕业设计源码是Java编程语言实现的一个教育领域管理系统,主要针对学校中的教职工教学活动进行管理和跟踪。这个系统通常包含多个模块,旨在提高教学管理的效率和准确性。以下是对该源码的一些详细解析...

    一个完整的Java做的B2C商城源码

    5. **安全性**:在易买网中,可能会使用Spring Security或Apache Shiro等安全框架,来处理用户认证、授权以及防止XSS、CSRF等安全攻击。 6. **购物车和订单系统**:B2C的核心功能之一是购物车和订单处理。这部分...

Global site tag (gtag.js) - Google Analytics