众所周知,在多个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。

(注:我猜想这样设计的目的在于方便二者的切换,毕竟二者之间只相差这个属性)

好啦,如果你读懂了我的结论,那么将会解决你的一部分困惑。

 

posted on 2020-04-26 13:54  追他十万八千里  阅读(1089)  评论(0编辑  收藏  举报