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 aggregate
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.
*
* 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. * * 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效果)
"authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator"> "realms"> <ref bean="jdbcRealm"/> <ref bean="jdbcRealm2"/>
"authenticationStrategy"> class="org.apache.shiro.authc.pam.FirstSuccessfulStrategy">
debug看一下(前提是配置了多个realm环境,并且可以debug到这一步):
可以看到该策略下(FirstSuccessfulStrategy),有一个stopAfterFirstSuccess属性,默认是false。(我就是看到这里才发现这个属性)。
默认false会是什么结果?
结果就是:
第一个realm即使验证通过,它还是会去验证第二个realm。
也就是说,当前的配置和AtLeastOneSuccessfulStrategy配置效果一样,基本没有变化。
可以看一下后台打印(两realm都被认证):
代码已经来到了:MyShiroRealm授权区域1-----> 通过realm-----没有异常! 代码已经来到了:MyShiroRealm授权区域2-----> 通过realm-----没有异常!
那么如果把stopAfterFirstSuccess属性改成true呢?
"authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator"> "realms"> <ref bean="jdbcRealm"/> <ref bean="jdbcRealm2"/>
"authenticationStrategy"> class="org.apache.shiro.authc.pam.FirstSuccessfulStrategy"> "stopAfterFirstSuccess" value="true">
再次debug:(观察变化)
这次stopAfterFirstSuccess属性的值为true,也就是说只要第一个realm通过认证,那么就不会匹配第二个realm了。
再看一下控制台打印,确实出现第一个通过认证的realm就不会接着认证(注:如果第一个realm没有被认证就会接着认证其它realm,直到出现第一个通过认证的realm):
代码已经来到了:MyShiroRealm授权区域1----->
通过realm-----没有异常!
总结:shiro-1.5.2想要看到FirstSuccessfulStrategy真正的效果,需要在spring的bean里配置stopAfterFirstSuccess属性为true,否则认证效果将等同于AtLeastOneSuccessfulStrategy。
(注:我猜想这样设计的目的在于方便二者的切换,毕竟二者之间只相差这个属性)
好啦,如果你读懂了我的结论,那么将会解决你的一部分困惑。