- 浏览: 236040 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
shuhucy:
必须赞啊,源码理解的很深,解决一个困扰两天的问题
Spring AOP源码分析(八)SpringAOP要注意的地方 -
sealinesu:
精彩
Spring事务源码分析(一)Spring事务入门 -
whlt20090509:
"WEB-INF/view目录下有一个简单的hell ...
SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门 -
hai0378:
兄台 算我一个,最近在研究dubbo motan 及 zk ...
ZooKeeper源码研究寻求小伙伴 -
zpkbtzmbw:
看懂了,原理
SpringMVC源码总结(五)Tomcat的URIEncoding、useBodyEncodingForURI和CharacterEncodingFilter
前两篇文章主要说的是认证过程,这一篇来分析下授权的过程。还是开涛大神的案例(http://jinnianshilongnian.iteye.com/blog/2020017),如下:
ini配置文件如下:
从subject.hasRole开始入手,默认的Subject为DelegatingSubject:
首先就是该用户是否已登录,验证角色的地方在securityManager的hasRole方法中:
AuthorizingSecurityManager实现了Authorizer接口,但是AuthorizingSecurityManager是通过内部Authorizer引用来完成具体的功能,默认采用的是ModularRealmAuthorizer。如下:
来看看这个Authorizer模块的接口设计:
从上面的接口中,可以分成两大类,第一类是验证用户的某个或某些权限,第二类是验证用户的某个角色或某些角色。角色则是一组权限的集合,所以后者是粗粒度的验证,而前者是细粒度的验证。对于那些check方法则是验证不通过时抛出异常。
接口实现类图为:
可以看到很多的Realm都实现了该接口,即这些Realm不仅提供登陆验证,还提供权限验证。
先来看下默认使用的ModularRealmAuthorizer:
可以看到,它有三个重要属性,Realm集合和PermissionResolver 、RolePermissionResolver 。PermissionResolver 是什么呢?
就是将权限字符串解析成Permission 对象,同理RolePermissionResolver 如下:
将角色字符串解析成Permission 集合。
来看下这几个方法:
看下这几个set方法,其目的都是如果哪些Realm 想要PermissionResolver 或RolePermissionResolver 参数,则将ModularRealmAuthorizer 的对应参数传给它。
再来看ModularRealmAuthorizer 是如何实现Authorizer接口的:
首先是判断Collection<Realm> realms集合是否为空,然后就是将那些实现了Authorizer接口的Realm 来判断是否具有某个权限,也就是ModularRealmAuthorizer本身并不去权限验证,而是交给那些具有权限验证功能的Realm去验证(即那些Realm实现了Authorizer接口)。所以
ModularRealmAuthorizer并不具有太多实际内容,我们转战那些实现了Authorizer接口的Realm,去看看他们的验证过程。
这时候,就需要看Authorizer接口的另一个分支即下图AuthorizingRealm分支:
AuthorizingRealm涉及到Realm,所以再把Realm说清楚。Realm接口如下:
Realm 本身只具有验证用户是否合法的功能,不具有授权的功能。再看它的实现者CachingRealm,从名字上就可以知道加入了缓存功能:
有3个对象属性和一个类属性,INSTANCE_COUNT 主要是用来计数Realm的个数的,同时追加到name属性中,cachingEnabled对外提供get、set方法,这里的cachingEnabled就相当于一个总开关,它的子类都有子开关,共同决定着是否进行缓存,如它的子类AuthenticatingRealm:
从这里就可以看到两个cacheEnabled的作用。也对外提供CacheManager的get、set方法, CachingRealm本身并没有做太多内容,就是把这几个参数收集起来,供子类去使用。
接下来看下Cache缓存的整体结构图:
我们要先看下CacheManager是干嘛的:
根据name获取一个Cache<K, V>这样的结构,看起来像HashMap的结构,这里的name到底是什么呢?
CachingRealm的子类AuthenticatingRealm有一个authenticationCacheName属性,而这里的authenticationCacheName就是我们刚才要找的目标,证据如下:
再看下authenticationCacheName的构成:
在创建AuthenticatingRealm时,authenticationCacheName 默认是当前类名+DEFAULT_AUTHORIZATION_CACHE_SUFFIX(为.authenticationCache)+数量。这个数量也是用来统计AuthenticatingRealm的个数的,这种方式仅仅是默认的,也可以去修改:
这两种方式都可以去修改。回到CacheManager:
然后就需要了解下Cache<K, V>这个结构:
这基本上不就是map的结构吗?为什么还要单独设计这样的结构呢?来看下它的文档介绍就知道了:
Shiro并不打算自己实现一个完整的缓存机制,因为这并不是安全框架的主要职责,相反它应该提供一个统一的API接口,可以加入不同缓存框架。而对于我们用户来说,只需针对这一层统一API进行编程,不再针对某个具体的缓存框架编程,这样就更加容易切换不同的缓存框架。
再看下,它的实现类MapCache和EhCache,MapCache很简单就是通过Map结构来实现
EhCache则是通过net.sf.ehcache.Ehcache框架来来实现,不再涉及。
Cache<K, V>知道了,又有哪些CacheManager的实现呢?
AbstractCacheManager如下:
也很简单,内部拥有一个ConcurrentHashMap集合,存取都是对该集合的操作,而把真正创建Cache的操作留给具体的子类来实现,即createCache方法。看下它的子类MemoryConstrainedCacheManager的createCache实现:
就是创建了一个MapCache对象作为Cache,至于SoftHashMap则需要单独去介绍其中的设计。
CachingRealm就大致介绍完了,回到它的子类,看它的子类AuthenticatingRealm是怎么去使用CacheManager。该子类主要完成认证流程,首先是其的初始化,AuthenticatingRealm及其子类都实现了Initializable接口,初始化的时候会首先获取其缓存,如下:
首先会获取Cache<Object, AuthenticationInfo> cache属性,如果没有,再判断是否允许缓存,如果允许,则通过CacheManager 来获取,之前已分析过,如果还没有则会创建一个Cache,然后返回。
再看下认证过程如下:
首先从缓存中尝试是否能找到AuthenticationInfo ,如果找不到,则需要子类去完成具体的认证细节,然后再存储到缓存中,因为本类并没有具体的数据源,只有缓存源,所以本类只是搭建了认证流程,具体的认证细节则由具体的子类来完成,所以 doGetAuthenticationInfo(token)是一个protected的抽象方法,如下:
当缓存中存在或者子类进行具体的认证后,下一步的操作是要进行密码匹配的过程,AuthenticatingRealm有一个属性CredentialsMatcher credentialsMatcher,接口如下:
就是匹配我们认证时的AuthenticationToken 和刚才已找到的AuthenticationInfo 是否匹配。有如下的实现类:AllowAllCredentialsMatcher、PasswordMatcher、SimpleCredentialsMatcher等等。AuthenticatingRealm的构造函数默认使用的是SimpleCredentialsMatcher:
这一块内容先暂时不讲,后续文章再来详细说明。
当你匹配通过了,则就算认证成功了。认证流程就在AuthenticatingRealm中完成了。
我们再向它的子类AuthorizingRealm研究,这个就有涉及到授权的功能了。AuthenticatingRealm是将整个认证流程框架化,AuthorizingRealm则是将整个授权流程框架化,AuthorizingRealm也有授权缓存,所以会通过父类CachingRealm来获取CacheManager,同时也有一个子缓存开关authorizationCachingEnabled,和AuthenticatingRealm基本类似,属性如下:
AtomicInteger 同样是用于对那些具有授权功能的Realm进行数量统计的,authorizationCachingEnabled缓存子开关,authorizationCache缓存,authorizationCacheName缓存名字。 PermissionResolver permissionResolver、RolePermissionResolver permissionRoleResolver这两个则是对字符串进行解析对应的Permission和Collection<Permission>的。我们来看下AuthorizingRealm的主要功能,对于授权接口Authorizer的实现:
首先就是获取授权信息,看下getAuthorizationInfo:
同样很容易理解,先得到缓存,从缓存中去找有没有授权信息,如果没有,则需要子类去完成具体的授权细节即doGetAuthorizationInfo,授权完成后放置缓存中。同样doGetAuthorizationInfo是protected的抽象方法,由子类去实现。PermissionResolver permissionResolver、RolePermissionResolver permissionRoleResolver则是发挥如下作用:
即有了授权信息AuthorizationInfo 后,获取所有的权限Permission,有三种途径来收集,第一种就是info.getObjectPermissions() info中直接含有Permission对象集合,第二种就是info.getStringPermissions() info中有字符串形式的权限表示,第三种就是info.getRoles() info中含有角色集合,角色也是一组权限的集合,看下resolvePermissions(info.getStringPermissions()):
也很简单,对于每一个strPermission 通过PermissionResolver 转化成Permission 对象,对于resolveRolePermissions也同理,不再说明。这里具体的转化细节先暂且不说,后续再将。
现在终于把认证流程和授权框架流程大致说完了,即AuthenticatingRealm和AuthorizingRealm的内容,他们分别留给子类protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;具体的认证方法和protected abstract AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)具体的授权方法。
作者:乒乓狂魔
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)具体的授权方法。
作者:乒乓狂魔
发表评论
-
shiro源码分析(十一)SecurityManager
2015-01-16 05:59 0shiro源码分析(十一)SecurityManager -
shiro源码分析(九)RememberMe分析
2015-01-04 08:20 0shiro源码分析(八)RememberMe分析shiro源码 ... -
shiro源码分析(七)对称式加密解密
2015-01-01 10:29 0shiro源码分析(七)对称式加密解密shiro源码分析(七) ... -
shiro源码分析(八)ini配置文件的解析原理
2014-12-30 06:17 0shiro源码分析(七)ini配置文件的解析shiro源码分析 ... -
shiro源码分析(六)CredentialsMatcher 的案例分析
2015-01-04 07:39 7713有了上一篇文章的原理分析,这一篇文章主要结合原理来进行使用。 ... -
shiro源码分析(四)具体的Realm
2014-12-24 07:28 5251首先还是Realm的接口设计图: 这里只来说明Simple ... -
shiro源码分析(五)CredentialsMatcher
2014-12-29 07:39 11686Realm在验证用户身份的时候,要进行密码匹配。最简单的情况就 ... -
Subject Runas模块
2014-12-17 07:05 0Subject Runas模块Subject Runas模块S ... -
shiro源码分析(二)Subject和Session
2014-12-15 08:02 16766继续上一篇文章的案例 ... -
2 MapContext分析
2014-12-11 07:13 02 MapContext分析 -
1 AuthenticationInfo 是如何进行合并的?
2014-12-11 07:12 01 AuthenticationInfo 是如何进行合并的? -
shiro源码分析(一)入门
2014-12-11 07:21 13870最近闲来无事,准备读 ...
相关推荐
- **源码分析**:通过阅读Shiro的源码,可以理解其内部的工作机制,包括如何进行身份验证、授权以及会话管理,有助于定制化开发和性能优化。 - **设计模式**:Shiro的源码中大量运用了设计模式,如工厂模式、代理...
通过对 Shiro1.2.2 的源码分析,我们可以了解到它如何处理各种安全问题,以及如何构建高效、灵活的安全架构。同时,源码的学习有助于我们理解和解决在实际开发中遇到的安全问题,提升我们的编程技能。
"跟我学Shiro源码"这个主题旨在深入理解Shiro的工作原理,通过分析源码来提升我们对安全编程的认知。 1. **Shiro架构** Shiro 的核心组件包括 Realm(认证与授权信息源)、SecurityManager(安全管理器)、Subject...
Shiro设计的核心理念是简洁,它将安全相关的功能划分为三个主要组件:认证(Authentication)、授权(Authorization)和会话管理(Session Management)。此外,它还提供加密服务来处理敏感数据。 2. **认证...
Apache Shiro 是一款轻量级的安全框架,专为Java应用设计,提供了认证、授权、会话管理和加密等核心安全功能。本课程“Shiro入门到精通”旨在帮助开发者全面理解和掌握Shiro的使用,通过与Spring Security的对比,...
SpringBoot和Shiro是两个非常重要的...通过以上分析,我们可以看出这个源码项目涵盖了企业级Web开发的多个方面,包括前后端分离、数据库设计、安全控制等,对于学习和实践SpringBoot及Shiro的使用具有很高的参考价值。
这个名为"SpringBoot+Shiro权限管理系统脚手架"的项目,为初学者提供了一个深入理解软件开发流程的绝佳实践平台,涵盖需求分析、系统设计、编码实现到测试部署的各个环节。 SpringBoot是基于Spring框架的简化版本,...
Apache Shiro是一个强大且易用的Java安全框架,提供了认证、授权、加密和会话管理功能,可以非常容易地开发出足够安全的应用。在这个电商书城系统中,Shiro主要负责用户的身份验证(Authentication)、授权...
本文将深入探讨一个基于SpringBoot框架设计的生鲜超市管理软件的实现,通过源码分析,帮助读者理解其设计思路和技术要点。 首先,SpringBoot是Java领域广泛使用的轻量级框架,它简化了Spring应用的初始搭建以及开发...
6. **源码分析**:压缩包中的“springboot_ym”可能是项目的源代码目录,包含了Java后端和Vue前端的代码。通过阅读源码,可以深入理解每个组件如何协同工作,如SpringBoot的配置、Shiro的安全逻辑、JWT的生成与验证...
这个源码通常包含了完整的前后端代码,以及数据库模型、接口设计、业务逻辑和用户体验等多个方面的实现。下面将详细介绍Java商城源码的相关知识点。 1. **Java技术栈**:Java商城源码的开发主要依赖Java技术栈,...
本文将深入探讨一个基于SpringBoot的权限管理系统,该系统易读易懂,界面设计简洁美观,核心技术栈包括Spring、MyBatis和Shiro。无需复杂的配置和额外的依赖,使得开发者可以快速上手并投入实际项目使用。 首先,...
7. **安全机制**:源码中可能包含了用户认证和授权的实现,如使用Spring Security或Apache Shiro,确保只有合法用户能访问特定资源。 8. **缓存策略**:为了提高性能,源码可能会使用Redis或Memcached等缓存技术来...
8. **权限管理**:对于大型门户网站,权限控制是必要的,如Spring Security或Apache Shiro,可以实现用户认证和授权。 9. **缓存机制**:如Redis或Memcached,用于存储常用数据,提高网站响应速度,减轻服务器压力...
- **Shiro**:安全框架,处理用户认证、授权和会话管理,`ehcache-shiro.xml`涉及到Shiro的缓存配置。 - **IKAnalyzer**:中文分词器,用于提高全文检索和搜索的准确度,`IKAnalyzer.cfg.xml`是其配置文件。 3. *...
【Shiro】是Apache的一个开源安全框架,主要负责认证、授权、会话管理和并发控制等功能。在Spring Boot项目中,Shiro提供了一种轻量级的安全解决方案,用于保护应用程序的资源,例如API接口或页面。 【MyBatis】是...
6. **安全机制**:为了保护用户数据和系统安全,源码可能包含了用户认证、授权、防止SQL注入、XSS攻击的防护措施,比如使用Spring Security或Apache Shiro。 7. **文件上传与下载**:论坛系统可能包含文件上传功能...
《基于JSP、SpringMVC和Hibernate的生离校就业分析管理平台源码解析》 在信息化时代,教育管理系统的建设对于高校来说至关重要,而基于JSP、SpringMVC和Hibernate构建的生离校就业分析管理平台正是这样一个高效、...
教职工教学管理毕业设计源码是Java编程语言实现的一个教育领域管理系统,主要针对学校中的教职工教学活动进行管理和跟踪。这个系统通常包含多个模块,旨在提高教学管理的效率和准确性。以下是对该源码的一些详细解析...
5. **安全性**:在易买网中,可能会使用Spring Security或Apache Shiro等安全框架,来处理用户认证、授权以及防止XSS、CSRF等安全攻击。 6. **购物车和订单系统**:B2C的核心功能之一是购物车和订单处理。这部分...