【Apache Shiro】學習筆記——Authentication基礎

從Authentication一步步學習。 j_0003.gif

wKioL1N8fCLgEZtQAABxbR1PGeM813.jpg

先從代碼開始,運行後再慢慢研究。

以下是我添加的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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章