在上篇Shiro核心概念中,咱們介紹了Shiro的三大核心概念:Subject
、SecurityManager
、Realms
,並經過示例代碼QuickStart
展現經常使用的認證、鑑權、退出的操做。數據庫
本篇咱們將深刻到Shiro的源碼中,探究Shiro底層實現認證的過程,作到「知其然知其因此然」。segmentfault
1 認證原理緩存
1.1 Principals與Credentialsui
認證就是進行身份確認的過程,也就是用戶(對應Shiro中的Subject
)須要提供證實來證明本身的身份。就像到自動取款機取款,持有銀行卡的人就能夠理解爲此處的用戶,銀行卡的取款密碼就是證實材料,若是輸入正確的密碼,就能夠進行取款。在這個過程當中,有兩個概念,用戶和證實材料,對應Shiro中的就分別是Principals與Credentials。spa
1.2 認證步驟代理
要進行認證,咱們須要先收集用戶的Principals與Credentials,好比用戶經過頁面上的表單提交用戶名和密碼,APP用戶經過提交手機號與短信驗證碼,而後交由服務端進行處理。code
①服務端首先收集Principals與Credentials,對應Shiro的代碼對象
UsernamePasswordToken token = new UsernamePasswordToken("username", "passwd");
這裏咱們使用Shiro中經過的用戶名/密碼認證方式,或者你能夠實現AuthenticationToken
接口來自定義blog
②接下來進行提交,對應代碼token
Subject currentUser = SecurityUtils.getSubject(); currentUser.login(token);
③認證結果
if (currentUser.isAuthenticated()) { // success do something } else { // fail throw exception }
1.3 認證原理
在瞭解了Shiro認證過程的基本代碼操做後,咱們來看下底層是到底如何實現。首先咱們先經過Shiro官方給出的一張認證流程圖來做全局的瞭解,看看底層認證都涉及到了哪些東西。
① 獲取Subject
對象,若是還不瞭解如何獲取Subject
對象,能夠回頭去看下咱們第一篇文章的介紹,或者下載示例代碼進行了解;而後收集用戶的認證資料,調用Subject
對象的login(token)
方法。
② DelegatingSubject
做爲Subject
的實現,自己並不負責處理認證與受權的邏輯,而是將方法的調用傳遞給底層的SecurityManager
,本質上說,DelegatingSubject
只是SecurityManager
的代理類,①中login(token)
方法的調用,本質上調用調用的是SecurityManager
接口的login(token)
方法,而DefaultSecurityManager
做爲SecurityManager
的默認實現,將調用Authenticator
進行認證邏輯處理
③ Authenticator
接口是Shiro API中的主要入口之一,就是用來負責應用中的認證操做的,該類做爲頂級接口,只有一個authenticate(AuthenticationToken token)
方法,而ModularRealmAuthenticator
做爲Shiro默認的認證處理實現類將會接過認證處理的槍,經過doAuthenticate(AuthenticationToken token)
來進行認證操做,源碼以下
Collection<Realm> realms = getRealms(); if (realms.size() == 1) { return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken); } else { return doMultiRealmAuthentication(realms, authenticationToken); }
④ 經過狀況下應用中會使用單個的Realm來進行認證受權處理,可是強大的Shiro卻支持配置多個Realm,在多個Realm對象存在的狀況下,就須要指定認證策略AuthenticationStrategy
,Shiro提供了三種具體的認證策略實現
AtLeastOneSuccessfulStrategy:ModularRealmAuthenticator的默認實現,多個Realm中,若是有一個或以上認證經過,就表示認證成功 FirstSuccessfulStrategy:只使用第一個認證經過的Realm返回的信息,後面的Realm將會被忽略 AllSuccessfulStrategy:全部Realm認證經過纔算認證成功,不然認證失敗
⑤ 經過Realm進行認證最終的邏輯判斷,咱們此處以應用只存在單個Realm來進行介紹。Realm首先會經過realm.supports(token)
進行驗證,驗證Realm是否支持對應的token進行認證操做,若是返回true,將會進行認證邏輯處理,不然直接忽略認證邏輯,若是咱們的應用只想處理受權,能夠自定義Realm,並將supports方法返回false便可。
Realm會經過token與INI配置文件中的配置項進行對比,或者與咱們數據庫存儲的數據進行對比,若是相同則認證經過。
下一小節,咱們將經過IniRealm來介紹Shiro是如何進行認證邏輯判斷的
1.4 IniRealm認證明現
在上篇文章中,咱們提到,Shiro默認使用IniRealm,可是前提是咱們在INI配置中指定了[users]或[roles]有效配置數據,不然就會用配置中指定的securityManager的realms,若是二者都沒有指定那麼就會拋出錯誤,由於Shiro應用,至少要配置一個Realm
IniRealm在初始化onInit()
時,會將已經加載的INI文件中的[users]、[roles]配置進行處理,分別轉換爲SimpleRole、SimpleAccount,再將SimpleAccount與SimpleRole進行綁定,至此,IniRealm對INI配置文件處理已經完畢。
可是認證的操做並無完成,IniRealm仍須要與傳遞過來的token進行對比,判斷是否相同,具體的判斷邏輯交由AuthenticatingRealm
來進行。
1.5 AuthenticatingRealm
AuthenticatingRealm是Realm的頂級抽象實現類,主要用於處理認證操做,至於受權等操做則交由該類的子類去處理。
AuthenticatingRealm拿到token後,會先去緩存中查找是否存在對應的認證信息,若是存在直接使用緩存中的認證信息與token進行比對,若是緩存中不存在,則直接獲取IniRealm中的認證信息進行比對,比對經過後,返回認證成功的Subject對象。
至此,認證的總體過程與底層邏輯已經所有介紹完畢。若是文章有什麼不足之處,或者說的不正確的地方,也但願各位小夥伴們與我聯繫和溝通。