Shiro多realm验证失败抛出错误异常

可紊 提交于 2019-12-18 21:16:44

【推荐】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;
    }
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!