權限管理 第2課 Shiro登入的源碼解析
權限管理 第1課 Shiro最簡demo
權限管理 第2課 Shiro登入的源碼解析
我們上一課通過ini配置方式實作了最簡的shiro登入的操作,可是具體是怎麼實作操作的呢?我們在這一章通過debug方式看看shiro登入的實作原理
我們仍舊基于上一課的代碼來作為例子。
1. 首先大體了解一下執行流程
我們在剖析源碼之前先看一張圖,這張圖講述了Shiro登入過程中執行的流程:
- 構造SecurityManager環境,就是上一課的通過SecurityManagerFactory建立SecurityManager對象,然後綁定到目前環境的操作;
- Subject.login(),就是上一課中将
作為參數的登入的部分,這裡将是我們本次深度剖析的重點;token
- 剩下的步驟我們沒有在上一節課看到,我們這次來慢慢剖析。
2. 開始debug源碼
1.
我們首先把斷點點在
subject.login();
登入的方法這兒,開始
debug
執行,執行到斷點卡住的時候,我們點選
F7
進入看一下。
2.
首先我們看到這次我們跳轉到了DelegatingSubject類,這是Subject的一個實作類。
1. 代碼停在了
this.clearRunAsIdentitiesInternal();
這個方法上,這個方法的作用是清除session中的已存在的登入資訊,我們既然是在登入,那麼就需要先将現在的登入狀态資訊清除掉;
2. 我們點選
F8
執行到下一步,就是圖中紅色方框的位置。我們看到這裡調用了
this.securityManager
對象的
login()
方法,就如上面流程圖中的第3步,我們
Subject.login()
方法其實是調用
securityManager.login()
方法實作的登入操作;
3. OK,我們繼續看一下
securityManager.login()
的實作原理吧,按
F7
進入
securityManager.login()
3.
我們看到代碼跳轉到了
DefaultSecurityManager
的類,是SecurityManager的實作類。我們先解析一下兩個對象:
1.
AuthenticationInfo
存儲的是主體(Subject)的身份認證資訊,說白了是
realm
的身份資訊。
2.
AuthenticationToken
用于收集使用者送出的身份(如使用者名)及憑據(如密碼),說白了是
Subject
傳入的身份資訊。
3. 我們看到此時方法中調用了一個
this.authenticate();
的身份認證方法,我們按
F7
進入繼續探究。
4.
到了這一步我們可以看到,此刻方法調用了
authenticator
對象的
authenticate()
認證方法。Authenticator正是Shiro的身份認證器。
也就是說一開始的
Subject.login()
調用了
SecurityManager.login()
方法,而
SecurityManager.login()
調用了
this.authenticate();
方法,而最終這個方法調用的是
Authenticator
的認證方法,繞了這麼一大圈,最後使用的還是
Authenticator
身份認證器進行認證登入的。
但是之是以
Subject
不能直接調用
Authenticator
身份認證器是因為所有的主體的操作都應該由安全管理器
SecurityManager
進行統一管理,隻開放對
Subject
的API接口,這樣才能使程式更加安全。
還沒完,我們繼續探究
Authenticator
身份認證器如何進行認證操作,我們按
F7
進入下一級。
5.
第70行,首先判斷token是否為空,為空的抛出異常
IllegalArgumentException
非法參數異常。
第77行,調用了一個身份驗證的方法,我們進去看看,按
F8
下一步到這裡,然後按
F7
進入。
6.
嗯?這一步我們終于看到了Shiro三大要素之一的
Realm
。
第107行,
this.assertRealmsConfigured();
是讀取資源配置,我們繼續往下執行,然後看一下reamls的資料:
哦?好熟悉的資料,好像是我們在
shiro.ini
配置的兩條參數。說明這裡已經把我們寫在配置檔案裡的模拟資料庫存儲的使用者的使用者名資訊讀取了出來。
我們繼續,到109行,這個方法将
realms
清單和
Subject
輸入的
AuthenticationToken
的對象傳了進去進行對比,然後傳回對比的結果,看看是否存在和
Subject
傳入的使用者名相同的資料。這裡比較繞,說白了就是去
realms
清單裡找
Subject
輸入的使用者名是否存在,如果存在,傳回存儲的正确的使用者密碼。具體的我們
F7
進去看看。
7. 使用者不存在異常
首先判斷傳入的token是否支援realm,不支援抛異常
UnsupportedTokenException
,Token不支援異常;
然後
realm.getAuthenticationInfo(token)
是用token的使用者名在realms裡尋找相同項,尋找到的話,傳回
realms
清單裡的那條資料,否則傳回
null
,這裡在下一步我們會詳細講解。
再然後,開始判斷,傳回的
info
資料,如果是
null
的話,說明
Subject
輸入的使用者名不存在,此處抛出異常
UnknownAccountException
。
是以我們之前在前一課的測試類裡捕獲登入抛出的這個異常時被定義為"使用者名不存在"的出處就在這裡。真的隐藏的好深是不是~~~
那麼下面繼續執行,我上圖是使用者名不存在的執行到了這一步,下面我會切換成密碼不對,再執行一次,接着示範,是以下面的講解是基于使用者名存在的情況下。
8. 密碼不正确異常
此處我們假設使用者名是對的,那麼我們要在上一步的第59行進去,那麼就得到了本步驟的圖,如果此時使用者名是存在的,那麼第一個紅框中傳回的info是有資料的。那麼就執行到了第二個紅框,下圖就是進入第二個紅框的代碼:
此處我們在第218行進入看一眼,就是下圖代碼,代碼裡就已經很明确了,分别從
Subject
的token和
Realm
的info提取憑證對象,然後進行判斷,這裡就是最終的憑證(一般是密碼)之間的對比了,傳回 true/false。
那麼傳回上面第二圖的第218行,如果傳回的是false,那麼就抛出異常
IncorrectCredentialsException
,不正确憑證異常。
我們之前在前一課的測試類裡捕獲登入抛出的這個異常時被定義為"密碼錯誤"的出處就在這裡。唉~ 心累~
9.
剩下的就順理成章啦,如果賬号密碼都對的話,就是可以在
realms
裡找到使用者名,并且密碼也是比對正确的,就直接登入成功啦,至于
session
之類的東西,我們後面再說,本章就到這裡。
如果本文有錯誤或對本文有不了解的地方歡迎評論 ^_^
如果本文有幫助到您,可以點一下右上角的贊哦,謝謝啦