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");]