Shiro sample中的源码分析:ios
建立SessionContext extends Map<String, Object>, 用其默认实现初始化: SessionContext sessionContext = new DefaultSessionContext(); 并设置请求host: sessionContext.setHost(host);缓存
利用 securityManager建立session: subject.securityManager.start(sessionContext);[note: securityManager将建立session任务代理到 SessionManager, securityManager.sessionManager.start(sessionContext);] 剖析AbstractNativeSecurityManager.start(sessionContext);session
private final DelegatingSubject owner;app
private StoppingAwareProxiedSession(Session target, DelegatingSubject owningSubject) { super(target); owner = owningSubject; }函数
public void stop() throws InvalidSessionException { super.stop(); owner.sessionStopped(); } } [note: 这里为何要用StoppingAwareProxiedSession包装delegatingSession呢?从实现上来看它继承ProxiedSession,并重写了stop()方法,目的有二: I) 经过StoppingAwareProxiedSession进一步代理DelegatingSession;II) DelegatingSubject中销毁session方法不能为public的,经过重写stop()方法使得能够在DelegatingSubjct销毁而且能够代理到ProxiedSession#stop() -> delegatingSession#stop(); -> 原始sesion#stop(); -> 无内存泄漏] 9. OK, 经过step 8的再次封装获得原始session的最终代理对象 StoppingAwareProxiedSession, 将其设置给DelegatingSubject,供程序猿调用: delegatingSubejct.session = StoppingAwareProxiedSession;源码分析
抽象出验证策略: AuthenticationInfo info = AbstractAuthenticator.doAuthenticate(AuthenticationToken); [留给Authenticator的具体实现完成]测试
若是step 1返回的AuthenticationInfo为空, 则抛出shiro自定义的验证异常: throw new AuthenticationException("refuse login sys"); -> 通知全部authenticator的监听者验证失败: notifyFailure(AuthenticationToken, AuthenticationException);ui
若是step 1中返回的AuthenticationInfo 不为空, 通知监听验证的全部观察者: notifySuccess(AuthenticationToken, AuthenticationInfo); -> 最终返回已经过认证的信息: return AuthenticationInfo; 模板式的身份验证已经呈如今上述3个步骤中, 接下来详细说下Authenticator的具体实现: ModularRealmAuthenticator.doAuthenticate(AuthenticationToken); 1. 先校验Authenticator中的Collection<Realm> 是否为空,若是为空, 则抛出异常: throw new IllegalStateException("Realm instance must exist at least one! Realm is used to execute AN authentication attemp!"); [note: Realm 做用是啥???先继续, 边读源码 边理解]this
2. 若是step 1中返回的Collection<Realm>#size() == 1 执行单个realm验证: doSingleRealmAuthentication(Realm, AuthenticationToken); 不然执行多realm 验证: doMultiRealmAuthentication(Collection<Realm>, AuthenticationToken); 这里先考虑单realm 验证: doSingleRealmAuthentication(Realm, AuthenticationToken); 剖析下单realm 验证明现: 1. 若是单个realm不支持用AuthenticationToken认证, 抛出异常: throw new UnSupportedTokenException("the realm instance does not support such an authentication token!"); 2. 若是单个realm支持 利用AuthenticationToken这样的认证, then execute realm authentication: IniRealm.getAuthenticationInfo(AuthenticationToken); -> AuthenticatingRealm.getAuthenticationInfo(AuthenticationToken); -> 先尝试从缓存中获得AuthenticationInfo, 缓存k,v => <Object, AuthenticationInfo> => <AuthenticationToken, AuthenticationInfo>: getCachedAuthenticationInfo(AuthenticationToken); -> 先想方设法获得Cache对象, 而后再从Cache中得到对应的AuthenticationInfo, 若是Cache中真的没有咱们想要的AuthenticationInfo, 那么就perform the lookup: doGetAuthenticationInfo(AuthenticationToken); 先看看怎么获得Cache对象: getCachedAuthenticationInfo(AuthenticationToken) -> getAvailableAuthenticationInfoCache() -> 确认是否能够缓存Authentication信息, 若是能够经过CacheManager.getCache(cacheName); 获得缓存: getAuthenticationCacheLazy(); -> getCacheManager().getCache(getAuthenticationCacheName()); 在本次测试代码中,是不容许缓存缓存Authentication信息的, 因此跳过了getCacheManager().getCache(getAuthenticationCacheName()); 直接获取 AuthenticationInfo: doGetAuthenticationInfo(AuthenticationInfo); [note: doGetAuthenticationInfo(AuthenticationToken); 为抽象方法, 用户须要自定义认证方法, 最终结果: 组装AuthenticationInfo或者抛出认证异常 => throw new AuthenticationException("认证失败");] -> 若是最终返回AuthenticationInfo instance, 尝试将其按键值对缓存<AuthenticationToken, AuthenticationInfo> [note: 本次不开启AuthenticationInfo缓存] -> 将获得的AuthenticationInfo 与 AuthenticationToken 作证书对比: CredentialMatcher.match(AuthenticationToken, AuthenticationInfo);[note: 若是二者credentials不符合,then throw new AuthenticationException("submitted credentials don't match the expected credentials");]