【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
使用Shiro时,采用默认验证策略AtLeastOneSuccessfulStrategy,在有多个realm验证下,如果有realm抛出异常后原始异常会被覆盖,导制部份异常显示非原始错误异常信息;
但因为有其它场景,又不能直接更改为AllSuccessfulStrategy(也就是所有Realm验证全部都要通过)。在阅读Shiro源码后,在没有其它合适的办法下,因此只好自行扩展一个MyModularRealmAuthenticator验证类;
增加此验证子类的目的是为了解决在多个Realm下,其中一个Realm抛异常后,继续验证其它Realm,导致原Realm异常被新的AuthenticationException覆盖问题,无法识别原始异常类型;
参见父类ModularRealmAuthenticator.doMultiRealmAuthentication()方法中的处理逻辑
异常如下:
org.apache.shiro.authc.AuthenticationException:
Authentication token of type [class org.apache.shiro.authc.UsernamePasswordToken] could not be authenticated by any configured realms.
Please ensure that at least one realm can authenticate these tokens.
MyModularRealmAuthenticator.java
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.pam.AuthenticationStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.realm.Realm;
import java.util.Collection;
import java.util.Iterator;
/**
* @Description 扩展父类原方法,捕获原始异常
*/
@Slf4j
public class MyModularRealmAuthenticator extends ModularRealmAuthenticator {
/**
* 扩展父类原方法,对异常立即进行外抛
* @param realms
* @param token
* @return
*/
protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) {
AuthenticationStrategy strategy = this.getAuthenticationStrategy();
AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token);
if (log.isTraceEnabled()) {
log.trace("Iterating through {} realms for PAM authentication", realms.size());
}
Iterator var5 = realms.iterator();
AuthenticationException authenticationException = null;
while(var5.hasNext()) {
Realm realm = (Realm)var5.next();
aggregate = strategy.beforeAttempt(realm, token, aggregate);
if (realm.supports(token)) {
log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm);
AuthenticationInfo info = null;
Throwable t = null;
try {
info = realm.getAuthenticationInfo(token);
} catch (Throwable var11) {
t = var11;
authenticationException = (AuthenticationException)var11;
if (log.isDebugEnabled()) {
String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:";
log.debug(msg, var11);
}
}
aggregate = strategy.afterAttempt(realm, token, info, aggregate, t);
//增加此逻辑,只有authenticationException不为null,则表示有Realm较验到了异常,则立即中断后续Realm验证直接外抛
if (authenticationException != null){
throw authenticationException;
}
} else {
log.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token);
}
}
aggregate = strategy.afterAllAttempts(token, aggregate);
return aggregate;
}
}
ShiroConfig.java
@Slf4j
@Configuration
public class ShiroConfig {
/**
* 初始化Authenticator验证管理器,如不注入,则会导致验证失败返回未登录
* Authorizer授权器:赋予主体有哪些权限
*/
@Bean
public Authenticator authenticator() {
//扩展父类原方法,捕获原始异常
ModularRealmAuthenticator authenticator = new MyModularRealmAuthenticator();
//设置两个Realm,一个用于用户登录验证和访问权限获取;一个用于jwt token的认证
authenticator.setRealms(Arrays.asList(jwtShiroRealm(), dbShiroRealm()));
/**
FirstSuccessfulStrategy:只要有一个 Realm 验证成功即可,只返回第一个 Realm 身份验证成功的认证信息,其他的忽略;
AtLeastOneSuccessfulStrategy:只要有一个 Realm 验证成功即可,和 FirstSuccessfulStrategy 不同,返回所有 Realm 身份验证成功的认证信息;(默认)
AllSuccessfulStrategy:所有 Realm 验证成功才算成功,且返回所有 Realm 身份验证成功的认证信息,如果有一个失败就失败了。
*/
//设置多个realm认证策略,一个成功即跳过其它的
authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
return authenticator;
}
}
来源:oschina
链接:https://my.oschina.net/u/437309/blog/3144616