shiro1.5.2中FirstSuccessfulStrategy正確配置方式


眾所周知,在多個realm環境下:

AuthenticationStrategy是一個接口,同時它的實現類對象,對應ModularRealmAuthenticator里的屬性。

AuthenticationStrategy接口有三個實現類:

看一下各自得定義吧:

FirstSuccessfulStrategy:只要有一個Realm驗證成功即可,只返回第一個Realm身份驗證成功的認證信息,其他的忽略;(注:這里"第一個"指的是認證成功得那一個realm)

AtLeastOneSuccessfulStrategy:只要有一個Realm驗證成功即可,和FirstSuccessfulSTRATEGY不同,將返回所有Realm身份驗證成功的認證信息;
AllSuccessfulStrategy:所有Realm驗證成功過才算成功,且返回所有Realm身份驗證的認證信息,如果有一個失敗就失敗。

而shiro的默認認證策略是AtLeastOneSuccessfulStrategy。

還有其它兩個認證策略:

我想說:AtLeastOneSuccessfulStrategy為默認配置,AllSuccessfulStrategy配置也比較簡單。

但是在最新版本1.5.2版shiro中,FirstSuccessfulStrategy里新加了個屬性,這個屬性在shiro1.3.2中不存在。

這個屬性是——stopAfterFirstSuccess,(如果不是通過debug發現了這個屬性,我到現在也不會得到正確結論)。

shiro1.5.3源碼:

源碼位置:shiro-root-1.5.2\core\src\main\java\org\apache\shiro\authc\pam

FirstSuccessfulStrategy.java

package org.apache.shiro.authc.pam;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.PrincipalCollection;

import java.util.Collection;

/**
 * {@link AuthenticationStrategy} implementation that only accepts the account data from
 * the first successfully consulted Realm and ignores all subsequent realms.  This is slightly
 * different behavior than {@link AtLeastOneSuccessfulStrategy}, so please review both to see
 * which one meets your needs better.
 *
 * @see AtLeastOneSuccessfulStrategy AtLeastOneSuccessfulAuthenticationStrategy
 * @since 0.9
 */
public class FirstSuccessfulStrategy extends AbstractAuthenticationStrategy {

    private boolean stopAfterFirstSuccess;

    public void setStopAfterFirstSuccess (boolean stopAfterFirstSuccess ) {

        this.stopAfterFirstSuccess  = stopAfterFirstSuccess ;
    }

    public boolean getStopAfterFirstSuccess() {
        return stopAfterFirstSuccess ;
    }

    /**
     * Returns {@code null} immediately, relying on this class's {@link #merge merge} implementation to return
     * only the first {@code info} object it encounters, ignoring all subsequent ones.
     */
    public AuthenticationInfo beforeAllAttempts(Collection<? extends Realm> realms, AuthenticationToken token) throws AuthenticationException {
        return null;
    }


    /**
     * Throws ShortCircuitIterationException if stopAfterFirstSuccess is set and authentication is 
     * successful with a previously consulted realm. 
     * Returns the <code>aggregate</code> method argument, without modification
     * otherwise.
     */
    public AuthenticationInfo beforeAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException {
        if (getStopAfterFirstSuccess() && aggregate != null && !isEmpty(aggregate.getPrincipals())) {
            throw new ShortCircuitIterationException();
        }
        return aggregate;
    }

    

    private static boolean isEmpty(PrincipalCollection pc) {
        return pc == null || pc.isEmpty();
    }

    /**
     * Returns the specified {@code aggregate} instance if is non null and valid (that is, has principals and they are
     * not empty) immediately, or, if it is null or not valid, the {@code info} argument is returned instead.
     * <p/>
     * This logic ensures that the first valid info encountered is the one retained and all subsequent ones are ignored,
     * since this strategy mandates that only the info from the first successfully authenticated realm be used.
     */
    protected AuthenticationInfo merge(AuthenticationInfo info, AuthenticationInfo aggregate) {
        if (aggregate != null && !isEmpty(aggregate.getPrincipals())) {
            return aggregate;
        }
        return info != null ? info : aggregate;
    }
}

再看看1.3.2源碼:

源碼位置:shiro-root-1.3.2\shiro\core\src\main\java\org\apache\shiro\authc\pam

FirstSuccessfulStrategy.java

package org.apache.shiro.authc.pam;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.util.CollectionUtils;

import java.util.Collection;

/**
 * {@link AuthenticationStrategy} implementation that only accepts the account data from
 * the first successfully consulted Realm and ignores all subsequent realms.  This is slightly
 * different behavior than {@link AtLeastOneSuccessfulStrategy}, so please review both to see
 * which one meets your needs better.
 *
 * @see AtLeastOneSuccessfulStrategy AtLeastOneSuccessfulAuthenticationStrategy
 * @since 0.9
 */
public class FirstSuccessfulStrategy extends AbstractAuthenticationStrategy {

    /**
     * Returns {@code null} immediately, relying on this class's {@link #merge merge} implementation to return
     * only the first {@code info} object it encounters, ignoring all subsequent ones.
     */
    public AuthenticationInfo beforeAllAttempts(Collection<? extends Realm> realms, AuthenticationToken token) throws AuthenticationException {
        return null;
    }

    /**
     * Returns the specified {@code aggregate} instance if is non null and valid (that is, has principals and they are
     * not empty) immediately, or, if it is null or not valid, the {@code info} argument is returned instead.
     * <p/>
     * This logic ensures that the first valid info encountered is the one retained and all subsequent ones are ignored,
     * since this strategy mandates that only the info from the first successfully authenticated realm be used.
     */
    protected AuthenticationInfo merge(AuthenticationInfo info, AuthenticationInfo aggregate) {
        if (aggregate != null && !CollectionUtils.isEmpty(aggregate.getPrincipals())) {
            return aggregate;
        }
        return info != null ? info : aggregate;
    }
}

通過debug獲得的真相

之前的源碼關聯錯了(關聯1.3.2源碼,而我導入的pom依賴是1.5.2,因此我怎么也找不到這個屬性,后來不經意間想起來可能是源碼的問題)

不過當時雖然看不到這個屬性,但我還是知道它存在,那么我是怎么知道有它呢?

首先需寫配置文件:

shiro1.3.2在applicationContext.xml里配置多realm認證環境方式:(注:1.5.2版本如果這樣配置的話,將無法獲得FirstSuccessfulStrategy效果)

    <!-- 多個realm時需要配置——ModularRealmAuthenticator-->
    <bean id ="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
        <property name="realms">
            <list>
                <ref bean="jdbcRealm"/>
                <ref bean="jdbcRealm2"/>
            </list>
        </property>
        <property name="authenticationStrategy">
            <bean class="org.apache.shiro.authc.pam.FirstSuccessfulStrategy"></bean>
        </property>
    </bean>

debug看一下(前提是配置了多個realm環境,並且可以debug到這一步):

可以看到該策略下(FirstSuccessfulStrategy),有一個stopAfterFirstSuccess屬性,默認是false。(我就是看到這里才發現這個屬性)。

默認false會是什么結果?

結果就是:

第一個realm即使驗證通過,它還是會去驗證第二個realm。

也就是說,當前的配置和AtLeastOneSuccessfulStrategy配置效果一樣,基本沒有變化。

可以看一下后台打印(兩realm都被認證):

代碼已經來到了:MyShiroRealm授權區域1----->
通過realm-----沒有異常!
代碼已經來到了:MyShiroRealm授權區域2----->
通過realm-----沒有異常!

那么如果把stopAfterFirstSuccess屬性改成true呢?

    <!-- 多個realm時需要配置——ModularRealmAuthenticator-->
    <bean id ="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
        <property name="realms">
            <list>
                <ref bean="jdbcRealm"/>
                <ref bean="jdbcRealm2"/>
            </list>
        </property>
        <property name="authenticationStrategy">
            <bean class="org.apache.shiro.authc.pam.FirstSuccessfulStrategy">
            <property name="stopAfterFirstSuccess" value="true"></property>
            </bean>
        </property>
    </bean>

再次debug:(觀察變化)

這次stopAfterFirstSuccess屬性的值為true,也就是說只要第一個realm通過認證,那么就不會匹配第二個realm了。

再看一下控制台打印,確實出現第一個通過認證的realm就不會接着認證(注:如果第一個realm沒有被認證就會接着認證其它realm,直到出現第一個通過認證的realm):

代碼已經來到了:MyShiroRealm授權區域1----->
通過realm-----沒有異常!

總結:shiro-1.5.2想要看到FirstSuccessfulStrategy真正的效果,需要在spring的bean里配置stopAfterFirstSuccess屬性為true,否則認證效果將等同於AtLeastOneSuccessfulStrategy。

(注:我猜想這樣設計的目的在於方便二者的切換,畢竟二者之間只相差這個屬性)

好啦,如果你讀懂了我的結論,那么將會解決你的一部分困惑。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM