1、AuthenticationStrategy实现
//在所有Realm验证之前调用 AuthenticationInfo beforeAllAttempts( Collection<? extends Realm> realms, AuthenticationToken token) throws AuthenticationException; //在每个Realm之前调用 AuthenticationInfo beforeAttempt( Realm realm, AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException; //在每个Realm之后调用 AuthenticationInfo afterAttempt( Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException; //在所有Realm之后调用 AuthenticationInfo afterAllAttempts( AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException;
因为每个AuthenticationStrategy实例都是无状态的,所有每次都通过接口将相应的认证信息传入下一次流程;通过如上接口可以进行如合并/返回第一个验证成功的认证信息。
自定义实现时一般继承org.apache.shiro.authc.pam.AbstractAuthenticationStrategy
下边这个OnlyOneAuthenticatorStrategy只允许一个成功的验证信息,进行返回,否则会报AuthenticationException,只返回第一个验证成功的Realm的认证信息
public class OnlyOneAuthenticatorStrategy extends AbstractAuthenticationStrategy { @Override public AuthenticationInfo beforeAllAttempts(Collection<? extends Realm> realms, AuthenticationToken token) throws AuthenticationException { return new SimpleAuthenticationInfo();//返回一个权限的认证信息,在进行验证之前返回的信息,不对权限信息进行处理 } @Override public AuthenticationInfo beforeAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException { return aggregate;//返回之前合并的,在进行验证之前返回的信息,不对权限信息进行处理
}
@Override
public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException {
AuthenticationInfo info;
if (singleRealmInfo == null)
{
info = aggregateInfo;
} else
{
if (aggregateInfo == null) {
info = singleRealmInfo;
} else {
info = merge(singleRealmInfo, aggregateInfo);
if(info.getPrincipals().getRealmNames().size() > 1)
{
System.out.println(info.getPrincipals().getRealmNames());
throw new AuthenticationException("Authentication token of type [" + token.getClass() + "] " +
"could not be authenticated by any configured realms. Please ensure that only one realm can " +
"authenticate these tokens.");
}
}
}
return info;
}
@Override
public AuthenticationInfo afterAllAttempts(AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException
{
return aggregate;
}
}
这里的shiro主要就是将多个AuthenticationToken信息合并在一起,
2、AbstractAuthenticationStrategy中的merge方法,主要就是先检查该
AuthenticationInfo 是不是可以merge的类型,如果是则将其转换为可以merge的类型,然后调用merge方法
/** * Merges the specified <code>info</code> argument into the <code>aggregate</code> argument and then returns an * aggregate for continued use throughout the login process. * <p/> * This implementation merely checks to see if the specified <code>aggregate</code> argument is an instance of * {@link org.apache.shiro.authc.MergableAuthenticationInfo MergableAuthenticationInfo}, and if so, calls * <code>aggregate.merge(info)</code> If it is <em>not</em> an instance of * <code>MergableAuthenticationInfo</code>, an {@link IllegalArgumentException IllegalArgumentException} is thrown. * Can be overridden by subclasses for custom merging behavior if implementing the * {@link org.apache.shiro.authc.MergableAuthenticationInfo MergableAuthenticationInfo} is not desired for some reason. */ protected AuthenticationInfo merge(AuthenticationInfo info, AuthenticationInfo aggregate) { if( aggregate instanceof MergableAuthenticationInfo ) { ((MergableAuthenticationInfo)aggregate).merge(info); return aggregate; } else { throw new IllegalArgumentException( "Attempt to merge authentication info from multiple realms, but aggregate " + "AuthenticationInfo is not of type MergableAuthenticationInfo." ); } }
可以进行多个AuthenticationInfo信息进行merge的功能如下所示,返回所有验证成功的Realm的认证信息
public class AtLeastTwoAuthenticatorStrategy extends AbstractAuthenticationStrategy { @Override public AuthenticationInfo beforeAllAttempts(Collection<? extends Realm> realms, AuthenticationToken token) throws AuthenticationException { return new SimpleAuthenticationInfo();//返回一个权限的认证信息 } @Override public AuthenticationInfo beforeAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException { return aggregate;//返回之前合并的 } @Override public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException { AuthenticationInfo info; if (singleRealmInfo == null) { info = aggregateInfo; } else { if (aggregateInfo == null) { info = singleRealmInfo; } else { info = merge(singleRealmInfo, aggregateInfo); } } return info; } @Override public AuthenticationInfo afterAllAttempts(AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException { if (aggregate == null || CollectionUtils.isEmpty(aggregate.getPrincipals()) || aggregate.getPrincipals().getRealmNames().size() < 2) { throw new AuthenticationException("Authentication token of type [" + token.getClass() + "] " + "could not be authenticated by any configured realms. Please ensure that at least two realm can " + "authenticate these tokens."); } return aggregate; }
3、对于Subject线程进行解绑
@After public void tearDown() throws Exception { ThreadContext.unbindSubject();//退出时请解除绑定Subject到线程 否则对下次测试造成影响 }