認證是Shiro最基本的工作!

先從代碼開始,運行后再慢慢研究。 以下是我添加的dependecies:
<!-- shiro --><dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>${shiro.version}</version></dependency><dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-sPRing</artifactId> <version>${shiro.version}</version></dependency><dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>${shiro.version}</version></dependency>在資源目錄下創建shiro.ini,文件內容為:
[users]king=t;stmdtkg寫一個main方法:package pac.testcase.shiro;
import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.ExcessiveAttemptsException;import org.apache.shiro.authc.IncorrectCredentialsException;import org.apache.shiro.authc.LockedAccountException;import org.apache.shiro.authc.UnknownAccountException;import org.apache.shiro.authc.UsernamePassWordToken;import org.apache.shiro.config.IniSecurityManagerFactory;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.subject.Subject;import org.apache.shiro.util.Factory;public class TestAuthen { public static void main(String[] args) { Factory<SecurityManager> factory = new IniSecurityManagerFactory(); SecurityManager manager = factory.getInstance(); SecurityUtils.setSecurityManager(manager); UsernamePasswordToken token = new UsernamePasswordToken("king", "t;stmdtkg"); token.setRememberMe(true); Subject currentUser = SecurityUtils.getSubject(); try { currentUser.login(token); } catch ( UnknownAccountException e ) { System.out.println("你是誰?"); } catch ( IncorrectCredentialsException e ) { System.out.println("密碼錯誤!!"); } catch ( LockedAccountException e ) { System.out.println("該賬戶不可用~"); } catch ( ExcessiveAttemptsException e ) { System.out.println("別再試了!!"); } currentUser.logout(); }}代碼非常好懂。 如果IniSecurityManagerFactory沒有指定配置文件,則
DEFAULT_INI_RESOURCE_PATH = "classpath:shiro.ini"。另外,SecurityUtils.setSecurityManager(manager);該方法是一個全局設置,設置整個VM單例的SecurityManager,一般開發中很少用到該方法。
另外,Shiro提供了豐富的Exception,我們可以根據不同的catch響應不同的提示。
關于身份驗證,有兩個重要的概念需要解釋:
一些類和方法的命名中如果出現這些詞匯不至于太陌生。
記錄一下身份驗證的具體步驟,用5個步驟簡單概括一下:

將代表用戶身份和憑證的token對象作為參數調用login方法。
Step 2.在上面的代碼中Subject對象實際上是作為一個用于委派(delegate)任務的對象——DelegatingSubject,以調用login(token)將驗證的任務委托給SecurityManager。SecurityManager調用
Subject login(Subject subject, AuthenticationToken authenticationToken)這里是驗證真正開始的地方。
Step 3.SecurityManager作為一個'umbrella'組件(這個比喻很流行嗎...),接收token并將任務委托給內部的 Authenticator(ps:SecurityManager extends Authenticator, Authorizer, sessionManager)并調用
public AuthenticationInfo authenticate(AuthenticationToken authenticationToken) throws AuthenticationException;通常情況下,這個authenticator是ModularRealmAuthenticator,ModularRealmAuthenticator可以在驗證時與多個realm進行交流(ModularRealmAuthenticator實現AuthenticationInfo doAuthenticate(AuthenticationToken token),上級AbstractAuthenticator在authenticate(token)中調用doAuthenticate(token))。
Step 4.如果配置了多個Realm,ModularRealmAuthenticator會用配置的驗證策略(AuthenticationStrategy)去進行多Realm驗證。
驗證策略與每一個Realm的結果交互。
如果只配置了一個Realm,驗證策略則沒有任何意義。
Step 5.調用Realm的boolean supports(AuthenticationToken token)確認Realm是否支持提交過來的token。
如果Realm支持提交過來的token,token將作為參數并調用:
AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;根據不同的token響應特定的驗證信息(AuthenticationInfo)。
當一個應用配置了多個Realm,ModularRealmAuthenticator通過其內部的AuthenticationStrategy去定義驗證策略(好像說了句廢話....命名確實很棒...)。
舉個例子: 如果一個Realm成功驗證了一個token,而其他的Realm全部失敗了,此時我們應該如何認定驗證是成功還是失敗?
是全部通過才算成功?還是說其中一個通過即可?或者說其中特定幾個通過后是否有必要繼續考慮其他?
接下來,試著用代碼體驗 multi-Realm與AuthenticationStrategy
Step 1.實現org.apache.shiro.realm.Realm來自定義幾個Realm。
在getAuthenticationInfo方法中直接返回驗證信息根本看不出驗證策略是否生效,于是我在一個Realm里拋出一個異常。
package pac.testcase.shiro.realm;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.IncorrectCredentialsException;import org.apache.shiro.authc.SimpleAuthenticationInfo;import org.apache.shiro.realm.Realm;public class MyRealm1 implements Realm { public String getName() { return this.getClass().getName(); } public boolean supports(AuthenticationToken token) { return true; } public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { if(!new String((char[])token.getCredentials()).equals("t;stmdtkg")) throw new IncorrectCredentialsException(); return new SimpleAuthenticationInfo(token.getPrincipal(),token.getCredentials(),this.getName()); }}Step 2.繼續使用本文開始時的代碼,將自定義的幾個Realm配置到shiro.ini中。
realm0=pac.testcase.shiro.realm.MyRealm0realm1=pac.testcase.shiro.realm.MyRealm1Step 3.在shiro.ini中配置驗證策略。
realm0=pac.testcase.shiro.realm.MyRealm0realm1=pac.testcase.shiro.realm.MyRealm1authcStrategy = org.apache.shiro.authc.pam.AllSuccessfulStrategysecurityManager.authenticator.authenticationStrategy = $authcStrategyStep 4.運行程序,輸出"密碼錯誤!!"
AuthenticationStrategy是無狀態的,處理一個驗證請求時AuthenticationStrategy會一共交互4次。
分別是:
另外,從所有成功的Realm中聚集結果并將其綁定到一個AuthenticationInfo也是AuthenticationStrategy的工作。
這個最后聚集起來的驗證信息則是Shiro用來表示Subject的標志,也就是所謂Principal。
具體體現在org.apache.shiro.authc.pam.ModularRealmAuthenticator中:
protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) { AuthenticationStrategy strategy = getAuthenticationStrategy(); AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token); if (log.isTraceEnabled()) { log.trace("Iterating through {} realms for PAM authentication", realms.size()); } for (Realm realm : realms) { aggregate = strategy.beforeAttempt(realm, token, aggregate); if (realm.supports(token)) { log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm); AuthenticationInfo info = null; Throwable t = null; try { info = realm.getAuthenticationInfo(token); } catch (Throwable throwable) { t = throwable; if (log.isDebugEnabled()) { String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:"; log.debug(msg, t); } } aggregate = strategy.afterAttempt(realm, token, info, aggregate, t); } else { log.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token); } } aggregate = strategy.afterAllAttempts(token, aggregate); return aggregate;}Shiro默認提供了三種AuthenticationStrategy實現:
ModularRealmAuthenticator默認采用AtLeastOneSuccessfulStrategy,如果想用其他的驗證策略則需要自行配置。
需要指出的是ModularRealmAuthenticator是按照某種順序與每個Realm進行交互的。
ModularRealmAuthenticator訪問配置于SecurityManager的Realm。
當處理一個驗證請求時會迭代Realm集合,調用所有支持當前token的Realm。
如果使用ini配置則會按照配置的順序去執行。
如果想要顯示定義Realm的順序,則在securityManager的屬性中按喜歡的順序設置一下即可。
比如:
securityManager.realms=$myRealm2,$myRealm1新聞熱點
疑難解答