国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Apache Shiro 學習記錄1

2019-11-15 00:36:33
字體:
來源:轉載
供稿:網友
Apache Shiro 學習記錄1

  最近幾天在學習Apache Shiro......看了一些大神們的教程.....感覺收獲不少.....但是畢竟教程也只是指引一下方向....即使是精品教程,仍然有很多東西都沒有說明....所以自己也稍微研究了一下...記錄了一下我的研究發現....教程點這里

  這篇教程的最后提到了strategy.....然后給出了4個方法.....但是并沒有怎么詳細說明.....我想說說我的理解.....(我的理解可能會有很多錯誤)


  我想先說說登陸驗證的大致流程....大致......

Subject

從用戶那里收集完用戶名密碼以后我們會調用subject.login(token)這個方法去登陸.....Subject是一個接口,沒有定義login的具體實現.....Shiro里只有一個類實現了這個接口,是DelegatingSubject這個類.這個類里的方法login方法如下:

 1    public void login(AuthenticationToken token) throws AuthenticationException { 2         clearRunAsIdentitiesInternal(); 3         Subject subject = securityManager.login(this, token); 4  5         PRincipalCollection principals; 6  7         String host = null; 8  9         if (subject instanceof DelegatingSubject) {10             DelegatingSubject delegating = (DelegatingSubject) subject;11             //we have to do this in case there are assumed identities - we don't want to lose the 'real' principals:12             principals = delegating.principals;13             host = delegating.host;14         } else {15             principals = subject.getPrincipals();16         }17 18         if (principals == null || principals.isEmpty()) {19             String msg = "Principals returned from securityManager.login( token ) returned a null or " +20                     "empty value.  This value must be non null and populated with one or more elements.";21             throw new IllegalStateException(msg);22         }23         this.principals = principals;24         this.authenticated = true;25         if (token instanceof HostAuthenticationToken) {26             host = ((HostAuthenticationToken) token).getHost();27         }28         if (host != null) {29             this.host = host;30         }31         session session = subject.getSession(false);32         if (session != null) {33             this.session = decorate(session);34         } else {35             this.session = null;36         }37     }

代碼各種復雜=.= ..............我也看不懂....但是我看到第三行,明白了subject其實也是讓securityManager來執行login操作的......那我們去看看securityManager吧......

SecurityManager

SecurityManager也是一個接口,各種繼承,實現其他接口......還好實現類只有一個DefaultSecurityManager類...并且login方法直到這個類才被實現....

 1     public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException { 2         AuthenticationInfo info; 3         try { 4             info = authenticate(token); 5         } catch (AuthenticationException ae) { 6             try { 7                 onFailedLogin(token, ae, subject); 8             } catch (Exception e) { 9                 if (log.isInfoEnabled()) {10                     log.info("onFailedLogin method threw an " +11                             "exception.  Logging and propagating original AuthenticationException.", e);12                 }13             }14             throw ae; //propagate15         }16 17         Subject loggedIn = createSubject(token, info, subject);18 19         onSuccessfulLogin(token, info, loggedIn);20 21         return loggedIn;22     }

我覺得這里最重要的就是第四行的authenticate方法.......

這個方法是在DefaultSecurityManager的N層父類AuthenticatingSecurityManager里實現的....

1     public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {2         return this.authenticator.authenticate(token);3     }

這里又可以看出SecurityManager的login方法其實也是委托給認證器authenticator調用authenticate(token)方法來實現的.

public abstract class AuthenticatingSecurityManager extends RealmSecurityManager {    /**     * The internal <code>Authenticator</code> delegate instance that this SecurityManager instance will use     * to perform all authentication Operations.     */    private Authenticator authenticator;    /**     * Default no-arg constructor that initializes its internal     * <code>authenticator</code> instance to a     * {@link org.apache.shiro.authc.pam.ModularRealmAuthenticator ModularRealmAuthenticator}.     */    public AuthenticatingSecurityManager() {        super();        this.authenticator = new ModularRealmAuthenticator();    }    ..................}

認證器authenticator的默認實現是ModularRealmAuthenticator....this.authenticator = new ModularRealmAuthenticator();...所以我們再來看看ModularRealmAuthenticator..........

ModularRealmAuthenticator

ModularRealmAuthenticator的authenticate方法是繼承自父類AbstractAuthenticator的..這個方法還是final的...

 1     public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException { 2  3         if (token == null) { 4             throw new IllegalArgumentException("Method argumet (authentication token) cannot be null."); 5         } 6  7         log.trace("Authentication attempt received for token [{}]", token); 8  9         AuthenticationInfo info;10         try {11             info = doAuthenticate(token);12             if (info == null) {13                 String msg = "No account information found for authentication token [" + token + "] by this " +14                         "Authenticator instance.  Please check that it is configured correctly.";15                 throw new AuthenticationException(msg);16             }17         } catch (Throwable t) {18             AuthenticationException ae = null;19             if (t instanceof AuthenticationException) {20                 ae = (AuthenticationException) t;21             }22             if (ae == null) {23                 //Exception thrown was not an expected AuthenticationException.  Therefore it is probably a little more24                 //severe or unexpected.  So, wrap in an AuthenticationException, log to warn, and propagate:25                 String msg = "Authentication failed for token submission [" + token + "].  Possible unexpected " +26                         "error? (Typical or expected login exceptions should extend from AuthenticationException).";27                 ae = new AuthenticationException(msg, t);28             }29             try {30                 notifyFailure(token, ae);31             } catch (Throwable t2) {32                 if (log.isWarnEnabled()) {33                     String msg = "Unable to send notification for failed authentication attempt - listener error?.  " +34                             "Please check your AuthenticationListener implementation(s).  Logging sending exception " +35                             "and propagating original AuthenticationException instead...";36                     log.warn(msg, t2);37                 }38             }39 40 41             throw ae;42         }43 44         log.debug("Authentication successful for token [{}].  Returned account [{}]", token, info);45 46         notifySuccess(token, info);47 48         return info;49     }

從中我們可以看出比較重要的是第11行info = doAuthenticate(token);

doAuthenticate(token)方法在ModularRealmAuthenticator之中被override過...

1     protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {2         assertRealmsConfigured();3         Collection<Realm> realms = getRealms();4         if (realms.size() == 1) {5             return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);6         } else {7             return doMultiRealmAuthentication(realms, authenticationToken);8         }9     }

這里還是比較明顯的,根據定義的Realm數量來決定是調用doSingleRealmAuthentication方法還是調用doMultiRealmAuthentication方法...

我們來看看doMultiRealmAuthentication方法里到底做了些什么呢..這個方法是定義在ModularRealmAuthenticator里的...

 1     protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) { 2  3         AuthenticationStrategy strategy = getAuthenticationStrategy(); 4  5         AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token); 6  7         if (log.isTraceEnabled()) { 8             log.trace("Iterating through {} realms for PAM authentication", realms.size()); 9         }10 11         for (Realm realm : realms) {12 13             aggregate = strategy.beforeAttempt(realm, token, aggregate);14 15             if (realm.supports(token)) {16 17                 log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm);18 19                 AuthenticationInfo info = null;20                 Throwable t = null;21                 try {22                     info = realm.getAuthenticationInfo(token);23                 } catch (Throwable throwable) {24                     t = throwable;25                     if (log.isDebugEnabled()) {26                         String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:";27                         log.debug(msg, t);28                     }29                 }30 31                 aggregate = strategy.afterAttempt(realm, token, info, aggregate, t);32 33             } else {34                 log.debug("Realm [{}] does not support token {}.  Skipping realm.", realm, token);35             }36         }37 38         aggregate = strategy.afterAllAttempts(token, aggregate);39 40         return aggregate;41     }

雖然很多看不明白....但是我大致流程能看懂:

在調用每個Realm之前,先執行strategy.beforeAllAttempts(realms, token);

然后再進入for (Realm realm : realms),就是準備去調用每個realm來認證..不過每個Realm認證之前還要調用aggregate = strategy.beforeAttempt(realm, token, aggregate);

然后得到單個Realm的認證結果info = realm.getAuthenticationInfo(token);

再調用aggregate = strategy.afterAttempt(realm, token, info, aggregate, t);這里可能會把info的認證信息和aggregate合并,就是把principle合并....不過還是要看具體的strategy的.

最后再調用aggregate = strategy.afterAllAttempts(token, aggregate);

也就是說strategy.beforeAllAttempts(realms, token);調用1次,然后根據realm的數量調用N次strategy.beforeAttempt(realm, token, aggregate);N次realm.getAuthenticationInfo(token);N次strategy.afterAttempt(realm, token, info, aggregate, t);再調用1次strategy.afterAllAttempts(token, aggregate);

另外,不是說你單個Realm里認證失敗,比如拋出了UnknownAccountException..所有認證就終止了....仔細看第23,24行會發現Realm里拋出的異常是會被攔截下來的...然后傳給了strategy.afterAttempt(realm, token, info, aggregate, t);....發現了這個異常以后到底是算認證失敗了還是繼續看其他的Realm是根據strategy來的...比如AllSuccessfulStrategy的afterAttempt方法...發現了任意1個realm拋出的異常,就直接拋出異常,終止認證....但是AtLeastOneSuccessfulStrategy就不一樣...它就會無視這個異常...繼續下面realm來認證...然后根據afterAllAttempts里的AuthenticationInfo的匯總信息來判斷到底是認證失敗了要拋出異常還是認證是成功的....

Shiro默認的策略是AtLeastOneSuccessfulStrategy


各種Strategy

我覺得看到這里我們可以再來看看Shiro已經定義的三種Strategy了...教程里提到過:

FirstSuccessfulStrategy:只要有一個Realm驗證成功即可,只返回第一個Realm身份驗證成功的認證信息,其他的忽略;

AtLeastOneSuccessfulStrategy:只要有一個Realm驗證成功即可,和FirstSuccessfulStrategy不同,返回所有Realm身份驗證成功的認證信息;

AllSuccessfulStrategy:所有Realm驗證成功才算成功,且返回所有Realm身份驗證成功的認證信息,如果有一個失敗就失敗了。

這3種策略都繼承自AbstractAuthenticationStrategy類..這個抽象類里定義了前面提到的2個after方法和2個before方法還有1個merge方法...

1     protected AuthenticationInfo merge(AuthenticationInfo info, AuthenticationInfo aggregate) {2         if( aggregate instanceof MergableAuthenticationInfo ) {3             ((MergableAuthenticationInfo)aggregate).merge(info);4             return aggregate;5         } else {6             throw new IllegalArgumentException( "Attempt to merge authentication info from multiple realms, but aggregate " +7                       "AuthenticationInfo is not of type MergableAuthenticationInfo." );8         }9     }

merge方法會調用AuthenticationInfo的merge方法....大致過程是:

  public void merge(AuthenticationInfo info) {        if (info == null || info.getPrincipals() == null || info.getPrincipals().isEmpty()) {            return;        }        if (this.principals == null) {            this.principals = info.getPrincipals();        } else {            if (!(this.principals instanceof MutablePrincipalCollection)) {                this.principals = new SimplePrincipalCollection(this.principals);            }            ((MutablePrincipalCollection) this.principals).addAll(info.getPrincipals());        }    ......................}

大致意思就是把info的principals合并到aggregate的principals上去....

AbstractAuthenticationStrategy的merge方法主要是在afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t)里被調用,因為經過一個Realm的認證以后會得到info對象...這個時候不同的認證策略就需要考慮是不是要把當前得到的info合并到已經有的前面幾個Realm積累下來的aggregateInfo里去了...

舉例:

AllSuccessfulStrategy類

 1     public AuthenticationInfo beforeAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException { 2         if (!realm.supports(token)) { 3             String msg = "Realm [" + realm + "] of type [" + realm.getClass().getName() + "] does not support " + 4                     " the submitted AuthenticationToken [" + token + "].  The [" + getClass().getName() + 5                     "] implementation requires all configured realm(s) to support and be able to process the submitted " + 6                     "AuthenticationToken."; 7             throw new UnsupportedTokenException(msg); 8         } 9 10         return info;11     }

第2行,如果realm不支持token,顯然這個realm是認證失敗的...而AllSuccessfulStrategy策略需要所有Realm都認證成功才行...果斷拋出異常...

 1     public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo info, AuthenticationInfo aggregate, Throwable t) 2             throws AuthenticationException { 3         if (t != null) { 4             if (t instanceof AuthenticationException) { 5                 //propagate: 6                 throw ((AuthenticationException) t); 7             } else { 8                 String msg = "Unable to acquire account data from realm [" + realm + "].  The [" + 9                         getClass().getName() + " implementation requires all configured realm(s) to operate successfully " +10                         "for a successful authentication.";11                 throw new AuthenticationException(msg, t);12             }13         }14         if (info == null) {15             String msg = "Realm [" + realm + "] could not find any associated account data for the submitted " +16                     "AuthenticationToken [" + token + "].  The [" + getClass().getName() + "] implementation requires " +17                     "all configured realm(s) to acquire valid account data for a submitted token during the " +18                     "log-in process.";19             throw new UnknownAccountException(msg);20         }21 22         log.debug("Account successfully authenticated using realm [{}]", realm);23 24         // If non-null account is returned, then the realm was able to authenticate the25         // user - so merge the account with any accumulated before:26         merge(info, aggregate);27 28         return aggregate;29     }

Throwable t, t是從Realm里拋出來的,如果Realm拋出了異常,不管什么原因,認證肯定是失敗了...所以Strategy也要拋出異常,即認證失敗了......info = null的道理也是一樣的....

AtLeastOneSuccessfulStrategy類:

 1     public AuthenticationInfo afterAllAttempts(AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException { 2         //we know if one or more were able to succesfully authenticate if the aggregated account object does not 3         //contain null or empty data: 4         if (aggregate == null || CollectionUtils.isEmpty(aggregate.getPrincipals())) { 5             throw new AuthenticationException("Authentication token of type [" + token.getClass() + "] " + 6                     "could not be authenticated by any configured realms.  Please ensure that at least one realm can " + 7                     "authenticate these tokens."); 8         } 9 10         return aggregate;11     }

所有Realm的認證都執行完畢之后,如果AuthenticationInfo 的合并結果aggregate還是null或者空的話那么說明所有Realm的認證都失敗的...那么就應該拋出異常,說明認證失敗了....

FirstSuccessfulStrategy類:

 1 public class FirstSuccessfulStrategy extends AbstractAuthenticationStrategy { 2  3     /** 4      * Returns {@code null} immediately, relying on this class's {@link #merge merge} implementation to return 5      * only the first {@code info} object it encounters, ignoring all subsequent ones. 6      */ 7     public AuthenticationInfo beforeAllAttempts(Collection<? extends Realm> realms, AuthenticationToken token) throws AuthenticationException { 8         return null; 9     }10 11     /**12      * Returns the specified {@code aggregate} instance if is non null and valid (that is, has principals and they are13      * not empty) immediately, or, if it is null or not valid, the {@code info} argument is returned instead.14      * <p/>15      * This logic ensures that the first valid info encountered is the one retained and all subsequent ones are ignored,16      * since this strategy mandates that only the info from the first successfully authenticated realm be used.17      */18     protected AuthenticationInfo merge(AuthenticationInfo info, AuthenticationInfo aggregate) {19         if (aggregate != null && !CollectionUtils.isEmpty(aggregate.getPrincipals())) {20             return aggregate;21         }22         return info != null ? info : aggregate;23     }24 }

為什么要overridebeforeAllAttempts方法返回null我也不懂....父類是返回newSimpleAuthenticationInfo();.....

override merge方法是因為FirstSuccessfulStrategy策略只返回第一個驗證成功的Realm的AuthenticationInfo ...

如果aggregate不是空的或者null,說明前面的Realm有成功過,那么根據策略,這個時候不應該把本次realm得到的info合并進去.....而是直接返回前面驗證成功的AuthenticationInfo ......


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 庆阳市| 沁源县| 商丘市| 五华县| 江油市| 张掖市| 天镇县| 娱乐| 龙游县| 民勤县| 牙克石市| 宁国市| 沅江市| 大荔县| 南江县| 会东县| 旺苍县| 霍林郭勒市| 子长县| 突泉县| 庄河市| 泸州市| 集贤县| 班玛县| 洛宁县| 互助| 铅山县| 洛川县| 疏勒县| 二手房| 娱乐| 惠水县| 五莲县| 长治市| 顺义区| 盱眙县| 桃园市| 缙云县| 西林县| 大竹县| 昌吉市|