Apache Shiro 使用手冊(二) Shiro 認證
一、Shiro認證過程
1、收集實體/憑據(jù)信息
//Example using most common scenario of username/password pair:
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//”Remember Me” built-in:
token.setRememberMe(true);
UsernamePasswordToken支持最常見的用戶名/密碼的認證機制。同時,由于它實現(xiàn)了RememberMeAuthenticationToken接口,我們可以通過令牌設(shè)置“記住我”的功能。
但是,“已記住”和“已認證”是有區(qū)別的:
已記住的用戶僅僅是非匿名用戶,你可以通過subject.getPrincipals()獲取用戶信息。但是它并非是完全認證通過的用戶,當你訪問需要認證用戶的功能時,你仍然需要重新提交認證信息。
這一區(qū)別可以參考亞馬遜網(wǎng)站,網(wǎng)站會默認記住登錄的用戶,再次訪問網(wǎng)站時,對于非敏感的頁面功能,頁面上會顯示記住的用戶信息,但是當你訪問網(wǎng)站賬戶信息時仍然需要再次進行登錄認證。
2、提交實體/憑據(jù)信息
Subject currentUser = SecurityUtils.getSubject();
currentUser.login(token);
收集了實體/憑據(jù)信息之后,我們可以通過SecurityUtils工具類,獲取當前的用戶,然后通過調(diào)用login方法提交認證。
3、認證處理
try {
currentUser.login(token);
} catch ( UnknownAccountException uae ) { ...
} catch ( IncorrectCredentialsException ice ) { ...
} catch ( LockedAccountException lae ) { ...
} catch ( ExcessiveAttemptsException eae ) { ...
} ... catch your own ...
} catch ( AuthenticationException ae ) {
//unexpected error?
}
如果login方法執(zhí)行完畢且沒有拋出任何異常信息,那么便認為用戶認證通過。之后在應(yīng)用程序任意地方調(diào)用SecurityUtils.getSubject() 都可以獲取到當前認證通過的用戶實例,使用subject.isAuthenticated()判斷用戶是否已驗證都將返回true.
相反,如果login方法執(zhí)行過程中拋出異常,那么將認為認證失敗。Shiro有著豐富的層次鮮明的異常類來描述認證失敗的原因,如代碼示例。
二、登出操作
登出操作可以通過調(diào)用subject.logout()來刪除你的登錄信息,如:
currentUser.logout(); //removes all identifying information and invalidates their session too.
當執(zhí)行完登出操作后,Session信息將被清空,subject將被視作為匿名用戶。
三、認證內(nèi)部處理機制
以上,是Shiro認證在應(yīng)用程序中的處理過程,下面將詳細解說Shiro認證的內(nèi)部處理機制。 
如上圖,我們通過Shiro架構(gòu)圖的認證部分,來說明Shiro認證內(nèi)部的處理順序:
1、應(yīng)用程序構(gòu)建了一個終端用戶認證信息的AuthenticationToken 實例后,調(diào)用Subject.login方法。
2、Sbuject的實例通常是DelegatingSubject類(或子類)的實例對象,在認證開始時,會委托應(yīng)用程序設(shè)置的securityManager實例調(diào)用securityManager.login(token)方法。
3、SecurityManager接受到token(令牌)信息后會委托內(nèi)置的Authenticator的實例(通常都是ModularRealmAuthenticator類的實例)調(diào)用authenticator.authenticate(token). ModularRealmAuthenticator在認證過程中會對設(shè)置的一個或多個Realm實例進行適配,它實際上為Shiro提供了一個可拔插的認證機制。
4、如果在應(yīng)用程序中配置了多個Realm,ModularRealmAuthenticator會根據(jù)配置的AuthenticationStrategy(認證策略)來進行多Realm的認證過程。在Realm被調(diào)用后,AuthenticationStrategy將對每一個Realm的結(jié)果作出響應(yīng)。
注:如果應(yīng)用程序中僅配置了一個Realm,Realm將被直接調(diào)用而無需再配置認證策略。
5、判斷每一個Realm是否支持提交的token,如果支持,Realm將調(diào)用getAuthenticationInfo(token); getAuthenticationInfo 方法就是實際認證處理,我們通過覆蓋Realm的doGetAuthenticationInfo方法來編寫我們自定義的認證處理。
四、使用多個Realm的處理機制:
1、Authenticator
默認實現(xiàn)是ModularRealmAuthenticator,它既支持單一Realm也支持多個Realm。如果僅配置了一個Realm,ModularRealmAuthenticator 會直接調(diào)用該Realm處理認證信息,如果配置了多個Realm,它會根據(jù)認證策略來適配Realm,找到合適的Realm執(zhí)行認證信息。
自定義Authenticator的配置:
[main]
...
authenticator = com.foo.bar.CustomAuthenticator
securityManager.authenticator = $authenticator
2、AuthenticationStrategy(認證策略)
當應(yīng)用程序配置了多個Realm時,ModularRealmAuthenticator將根據(jù)認證策略來判斷認證成功或是失敗。
例如,如果只有一個Realm驗證成功,而其他Realm驗證失敗,那么這次認證是否成功呢?如果大多數(shù)的Realm驗證成功了,認證是否就認為成功呢?或者,一個Realm驗證成功后,是否還需要判斷其他Realm的結(jié)果?認證策略就是根據(jù)應(yīng)用程序的需要對這些問題作出決斷。
認證策略是一個無狀態(tài)的組件,在認證過程中會經(jīng)過4次的調(diào)用:
在所有Realm被調(diào)用之前
在調(diào)用Realm的getAuthenticationInfo 方法之前
在調(diào)用Realm的getAuthenticationInfo 方法之后
在所有Realm被調(diào)用之后
認證策略的另外一項工作就是聚合所有Realm的結(jié)果信息封裝至一個AuthenticationInfo實例中,并將此信息返回,以此作為Subject的身份信息。
Shiro有3中認證策略的具體實現(xiàn):
| AtLeastOneSuccessfulStrategy | 只要有一個(或更多)的Realm驗證成功,那么認證將被視為成功 |
| FirstSuccessfulStrategy | 第一個Realm驗證成功,整體認證將被視為成功,且后續(xù)Realm將被忽略 |
| AllSuccessfulStrategy | 所有Realm成功,認證才視為成功 |
ModularRealmAuthenticator 內(nèi)置的認證策略默認實現(xiàn)是AtLeastOneSuccessfulStrategy 方式,因為這種方式也是被廣泛使用的一種認證策略。當然,你也可以通過配置文件定義你需要的策略,如:
[main]
...
authcStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy
securityManager.authenticator.authenticationStrategy = $authcStrategy
...
3、Realm的順序
由剛才提到的認證策略,可以看到Realm在ModularRealmAuthenticator 里面的順序?qū)φJ證是有影響的。
ModularRealmAuthenticator 會讀取配置在SecurityManager里的Realm。當執(zhí)行認證是,它會遍歷Realm集合,對所有支持提交的token的Realm調(diào)用getAuthenticationInfo 。
因此,如果Realm的順序?qū)δ闶褂玫恼J證策略結(jié)果有影響,那么你應(yīng)該在配置文件中明確定義Realm的順序,如:
blahRealm = com.company.blah.Realm
...
fooRealm = com.company.foo.Realm
...
barRealm = com.company.another.Realm
securityManager.realms = $fooRealm, $barRealm, $blahRealm
相關(guān)文章
Virtualbox安裝Lubuntu 18.04 64位的圖文教程
這篇文章主要為大家詳細介紹了Virtualbox安裝Lubuntu 18.04 64位的圖文教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-07-07
Linux(ubuntu)下實現(xiàn)增加/刪除文件權(quán)限
下面小編就為大家分享一篇Linux(ubuntu)下實現(xiàn)增加/刪除文件權(quán)限,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-04-04
CentoS6.5環(huán)境下redis4.0.1(stable)安裝和主從復制配置方法
這篇文章主要介紹了CentoS6.5環(huán)境下redis4.0.1(stable)安裝和主從復制配置方法,結(jié)合實例形式分析了CentoS6.5下redis4.0.1的安裝和主從復制配置相關(guān)步驟、命令與操作注意事項,需要的朋友可以參考下2018-04-04
php靜態(tài)化頁面 htaccess寫法詳解(htaccess怎么寫?)
如何讓自己的本地APACHE服務(wù)器支持”.htaccess”呢?其實只要簡單修改一下apache的httpd.conf設(shè)置就可以讓APACHE支持.htaccess了2012-02-02
Manjaro Linux安裝singularity-container的解決方案
這篇文章主要介紹Singularity容器在Manjaro平臺的安裝,以及一些常見的使用場景:拉取沙箱制作容器鏡像、遠程制作容器鏡像以及修改容器鏡像的方法,感興趣的朋友跟隨小編一起看看吧2024-05-05
apache中使用mod_log_slow分析響應(yīng)慢的請求
這篇文章主要介紹了apache中使用mod_log_slow分析響應(yīng)慢的請求,使用mod_log_slow可以定位到響應(yīng)慢的PHP代碼位置,需要的朋友可以參考下2014-06-06

