【Apache Shiro】學習筆記——Authentication基礎
從Authentication一步步學習。
先從程式碼開始,執行後再慢慢研究。
以下是我新增的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
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響應不同的提示。
關於身份驗證,有兩個重要的概念需要解釋:
・ Principals :身份(暫且這樣翻譯吧),Subject的唯一標識,可以是任何東西,比如使用者名稱或者e-mail地址。
・ Credentials :憑證,用於證明身份的東西,可以簡單理解為密碼。
一些類和方法的命名中如果出現這些詞彙不至於太陌生。
記錄一下身份驗證的具體步驟,用5個步驟簡單概括一下:
Step 1.
將代表使用者身份和憑證的token物件作為引數呼叫login方法。
Step 2.
在上面的程式碼中Subject物件實際上是作為一個用於委派(delegate)任務的物件――DelegatingSubject,以呼叫 login(token) 將驗證的任務委託給SecurityManager。
SecurityManager呼叫
Subject login (Subject subject, AuthenticationToken authenticationToken)
這裡是驗證真正開始的地方。
Step 3.
作為一個'umbrella'元件(這個比喻很流行嗎...),接收token並將任務委託給內部的Authenticator(ps: SecurityManager extends henticator, Authorizer, SessionManager )並呼叫 authenticate (token) 方法。
通常情況下,這個authenticator是 ModularRealmAuthenticator ,ModularRealmAuthenticator可以在驗證時與多個realm進行交流。
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.MyRealm0 realm1=pac.testcase.shiro.realm.MyRealm1
Step 3.
在shiro.ini中配置驗證策略。
realm0=pac.testcase.shiro.realm.MyRealm0
realm1=pac.testcase.shiro.realm.MyRealm1
authcStrategy = org.apache.shiro.authc.pam.AllSuccessfulStrategy
securityManager.authenticator.authenticationStrategy = $authcStrategy
Step 4.
執行程式,輸出"密碼錯誤!!"
AuthenticationStrategy 是無狀態的,處理一個驗證請求時AuthenticationStrategy會一共互動4次。
分別是:
・任何一個Realm執行之前。
・每個Realm的getAuthenticationInfo方法被呼叫之前。
・每個Realm的getAuthenticationInfo方法被呼叫之後。
・所有的Realm執行之後。
另外,從所有成功的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 實現:
・ AtLeastOneSuccessfulStrategy :其中一個通過則成功。
・ FirstSuccessfulStrategy :其中一個通過則成功,但只返回第一個通過的Realm提供的驗證資訊。
・ AllSuccessfulStrategy :凡是配置到應用中的Realm都必須全部通過。
ModularRealmAuthenticator預設採用AtLeastOneSuccessfulStrategy,如果想用其他的驗證策略則需要自行配置。
轉自:http://www.tuicool.com/articles/MVFRr2N