Authentication概述
n概述
Authentication 是指身份驗證的過程——即證明一個用戶實際上是不是他們所說的他們是誰。也就是說通過提交用戶的身份和憑證給Shiro,以判斷它們是否和應(yīng)用程序預(yù)期的相匹配。
n基本概念
1:Principals(身份):是Subject 的‘identifying attributes(標識屬性)’。比如我們登錄提交的用戶名。 2:Credentials(憑證):通常是只被Subject 知道的秘密值,它用來作為一種起支持作用的證據(jù),此證據(jù)事實上包含著所謂的身份證明。比如我們登錄提供的密碼
n認證的基本步驟
1. 收集Subjects 提交的Principals(身份)和Credentials(憑證); 2. 提交Principals(身份)和Credentials(憑證)進行身份驗證; 3. 如果提交成功,則允許訪問,否則重新進行身份驗證或者阻止訪問。
認證樣例
n使用用戶名/密碼的樣例
UsernamePasswordToken token = new UsernamePasswordToken(username, password); token.setRememberMe(true); 樣例使用UsernamePasswordToken 來支持最常見的用戶名/密碼的身份驗證方法。這是Shiro的org.apache.shiro.authc.AuthenticationToken 的接口,是Shiro 代表提交的Principals(身份)和Credentials(憑證)的身份驗證系統(tǒng)所使用的基本接口的一個實現(xiàn)。
n提交用戶名/密碼進行認證
Subject currentUser = SecurityUtils.getSubject(); currentUser.login(token);
n處理認證成功和失敗
如果認證成功,會沒有返回,也沒有例外,通過。 如果認證失敗,會拋出例外,你可以在程序中捕獲并處理,如下示例:
try { currentUser.login(token); } catch ( UnknownAccountException uae ) { … } catch ( IncorrectCredentialsException ice ) { … } catch (LockedAccountException lae ) { … } catch (ExcessiveAttemptsException eae ) { … } … catch your own …
nlogout(注銷)
currentUser.logout(); 當你調(diào)用logout,任何現(xiàn)有的Session 都將會失效,而且任何身份都將會失去關(guān)聯(lián)(例如,在Web 應(yīng)用程序中,RememberMe cookie 也將被刪除)。在Subject 注銷后,該Subject的實例被再次認為是匿名的,當然,除了Web 應(yīng)用程序。 注意:由于在Web 應(yīng)用程序記住身份往往是依靠Cookies,然而Cookies 只能在Response 被committed 之前被刪除,所以強烈建議在調(diào)用subject.logout()后立即將終端用戶重定向到一個新的視圖或頁面。 這樣能夠保證任何與安全相關(guān)的Cookies 都能像預(yù)期的一樣被刪除。這是HTTP cookies 的功能限制,而不是Shiro的。
Remembered和Authenticated
nRemembered(記住我)
一個記住我的Subject 不是匿名的,是有一個已知的身份ID(也就是subject.getPrincipals()是非空的)。但是這個被記住的身份ID 是在之前的session 中被認證的。如果subject.isRemembered()返回true,則Subject 被認為是被記住的。
nAuthenticated(已認證)
一個已認證的Subject 是指在當前Session 中被成功地驗證過了(也就是說,login方法被調(diào)用并且沒有拋出異常)。如果subject.isAuthenticated()返回true 則認為Subject 已通過驗證。
n注意他們是互斥的
Remembered 和Authenticated 是互斥的——若其中一個為真則另一個為假,反之亦然
認證順序
nStep 1:應(yīng)用程序代碼調(diào)用Subject.login 方法,傳遞創(chuàng)建好的包含終端用戶的Principals(身份)和Credentials(憑證)的AuthenticationToken 實例。
nStep 2:Subject實例,通常是DelegatingSubject(或子類)委托應(yīng)用程序的SecurityManager通過調(diào)用securityManager.login(token)開始真正的驗證。
nStep3:SubjectManager 接收token 以及簡單地委托給內(nèi)部的Authenticator 實例通過調(diào)用authenticator.authenticate(token)。這通常是一個ModularRealmAuthenticator 實例,支持在身份驗證中協(xié)調(diào)一個或多個Realm 實例。
n
nStep 4:如果應(yīng)用程序中配置了一個以上的Realm,ModularRealmAuthenticator 實例將利用配置好的AuthenticationStrategy 來啟動Multi-Realm 認證嘗試。在Realms 被身份驗證調(diào)用之前,期間和以后,AuthenticationStrategy 被調(diào)用使其能夠?qū)γ總€Realm 的結(jié)果作出反應(yīng)。
n
nStep 5:每個配置的Realm 用來幫助看它是否支持提交的AuthenticationToken。如果支持,那么支持Realm 的getAuthenticationInfo 方法將會伴隨著提交的token 被調(diào)用。getAuthenticationInfo 方法有效地代表一個特定Realm 的單一的身份驗證嘗試。
初識自定義
Realm
n這里先來個例子,認識一下:
public class MyRealm extends AuthorizingRealm{ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String userName = (String) getAvailablePrincipal(principals); //通過用戶名去獲得用戶的所有資源,并把資源存入info中 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); Set<String> s = new HashSet<String>(); s.add("p1"); s.add("p2"); info.setStringPermissions(s); Set<String> r = new HashSet<String>(); r.add("r1"); r.add("r2"); info.setRoles(r); return info;} protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { //token中儲存著輸入的用戶名和密碼 UsernamePasswordToken upToken = (UsernamePasswordToken)token; String username = upToken.getUsername(); String password = String.valueOf(upToken.getPassword()); //通常是與數(shù)據(jù)庫中用戶名和密碼進行比對,這里就省略了 //比對成功則返回info,比對失敗則拋出對應(yīng)信息的異常AuthenticationException SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password .toCharArray(),getName()); return info; }}
配置多個Realm
n上面的例子可以作為第一個Realm
n再復(fù)制一份,定義為MyRealm2,在返回user前添加拋出一個例外,表示認真沒有通過,如下:
if(username.equals("javass")){ throw new AuthenticationException("MyRealm2 認證失敗"); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password .toCharArray(),getName());
n在配置文件里面添加Realm的定義
myRealm1=cn.javass.hello.MyRealm myRealm2=cn.javass.hello.MyRealm2
由于有多個realm,一般就需要配置AuthenticationStrategy了,而AuthenticationStrategy是跟Authenticator(認證器)相關(guān)的。
n配置Authenticator和AuthenticationStrategy
authenticator = org.apache.shiro.authc.pam.ModularRealmAuthenticator authcStrategy = org.apache.shiro.authc.pam.AllSuccessfulStrategy authenticator.authenticationStrategy = $authcStrategy authenticator.realms=$myRealm2,$myRealm1
n當然,你可以擴展并實現(xiàn)自己的Authenticator,一般沒有必要
n最后把Authenticator設(shè)置給securityManager
securityManager.authenticator = $authenticator
n關(guān)于AuthenticationStrategy的配置,有三種:
AtLeastOneSuccessfulStrategy :如果一個(或更多)Realm 驗證成功,則整體的嘗試被認為是成功的。如果沒有一個驗證成功,則整體嘗試失敗。 FirstSuccessfulStrategy 只有第一個成功地驗證的Realm 返回的信息將被使用。所有進一步的Realm 將被忽略。如果沒有一個驗證成功,則整體嘗試失敗 AllSucessfulStrategy 為了整體的嘗試成功,所有配置的Realm 必須驗證成功。如果沒有一個驗證成功,則整體嘗試失敗。
ModularRealmAuthenticator 默認的是AtLeastOneSuccessfulStrategy
n自定義自己的AuthenticationStrategy,通常是擴展自AbstractAuthenticationStrategy,示例如下:
public class MyAuthenticationStrategy extends AbstractAuthenticationStrategy{ public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException { if(realm.getName().equals("myRealm2")){ if(singleRealmInfo==null || singleRealmInfo.getPrincipals()==null){ throw new AuthenticationException("主戰(zhàn)認證未通過"); } } return super.afterAttempt(realm, token, singleRealmInfo, aggregateInfo, t); } } 至于具體覆蓋擴展什么方法,需要根據(jù)你具體的策略來定。
多個Realm的驗證順序
n概述
非常重要的一點是:ModularRealmAuthenticator 將與Realm 實例以迭代的順序進行交互。 在SecurityManager 中已經(jīng)配置好了ModularRealmAuthenticator 對Realm實例的訪問。當執(zhí)行一個認證嘗試時,它將會遍歷該集合,并對每一個支持提交AuthenticationToken 的Realm 調(diào)用Realm 的getAuthenticationInfo 方法
n隱式排列
當你配置多個realm的時候,處理的順序默認就是你配置的順序。 這種情況通常就是只定義了realm,而沒有配置securityManager的realms
n顯示排列
也就是顯示的配置securityManager.realms,那么執(zhí)行的順序就是你配置該值的realm的順序。 通常更推薦顯示排列。 |
|
來自: wayne_liberary > 《Shiro》