Shiro Realm中自定义的异常如何捕获并返回给前端

Shiro Realm中自定义的异常如何捕获并返回给前端

问题引导

目前正在使用SpringBoot+Shiro+JWT进行系统的认证与鉴权开发,今天在开发的过程遇到了一个问题,特此记录。

众所周知,SpringBoot提供了一套强大的异常处理工具,我们习惯使用的组合是@RestControllerAdvise+@ExceptionHandler的方式对业务层抛出的异常进行统一处理。但是这个的适用前提是用户的请求能到达控制层,也就是需要保证这个请求不会在到达控制层之前被Filter以及Interceptor过滤或者拦截。
问题就在这里,Shiro或者说不只是Shiro,很多权限框架都是通过Filter以及Interceptor进行先进行权限认证后再允许用户访问对应的控制层接口,也就是说,对于Shiro来说实际上登录这个操作是在Filter进行的,那么在Filter中抛出的异常,是肯定到不了控制蹭的,这就是问题所在。

异常抛出

异常处理
没什么好说的,JsonResultException作为总的异常,其余内部业务继承这个总的,之后只需要在控制层捕获这个异常就ok了。

这一部分主要是用于后期代码理解

下面是Realm中的doGetAuthenticationInfo方法中的自定义异常抛出

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // .....省略1000行
        try{
            JwtUtil.getInstance().verifyToken(credentials, password);
        }catch(TokenExpiredException e){
            log.error("token已经过期[" + credentials + "]");
            throw new TokenException(JwtResultCode.TOKEN_EXPIRED);
        }catch (SignatureVerificationException e){
            log.error("token签证失效[" + credentials + "]");
            throw new AuthException(AuthResultCode.AUTH_ERROR);
        }catch(JWTVerificationException e){
            log.error("token无效[" + credentials + "]");
            throw new TokenException(JwtResultCode.TOKEN_INVALID);
        }
        // ....再省略1000行
    }

解决方案

先给出解决方案,具体原因在后面

Filter中的改动

    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        // 前置省略1000行
        try {
            // 尝试登录
            // login方法最终交由Realm中doGetAuthenticationInfo进行认证
            subject.login(token);
        }catch(AuthenticationException e){
        	// 这个cause有可能就是你的异常
        	// 所以如果是你的异常,那么就需要进行自行的处理
            Throwable cause = e.getCause();
            if(Objects.nonNull(cause) && cause instanceof  JsonResultException) {
                JsonResultException ex = (JsonResultException) cause;
                this.handleFailResponse(response, ex);
            }
            return false;
        }
        // 未抛出异常就返回true
        return true;
    }
    /**
     * 处理此Filter中的禁止通行的情况
     *
     * @param response ServletResponse
     * @param result 报错
     * @throws IOException 由于使用了writer可能会出现IO流报错
     */
    private void handleFailResponse(ServletResponse response, RuntimeException result){
        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
        httpServletResponse.setStatus(200);
        httpServletResponse.setContentType("application/json");
        httpServletResponse.setCharacterEncoding("UTF-8");
        try {
            httpServletResponse.getWriter().write(JSON.toJSONString(JsonResultUtil.getInstance().fail(result)));
            httpServletResponse.getWriter().flush();
        }catch(IOException e){
            log.error(e.getMessage());
        }
    }

解决思路

笔者完成Shiro配置的时候自定义的Filter是继承的BasicHttpAuthenticationFilter懂的都懂,这个继承哪个只要满足自己需求就行 ),实现了executeLogin方法,里面执行了subject.login()方法,这个方法内部调用了。那么既然我调用了subject.login(),在外层包try-catch捕获抛出的自定义异常在进行异常处理设置response返回给前端不就好了?于是就有了第一版解决方式

try {
    // 尝试登录
    // login方法最终交由Realm中doGetAuthenticationInfo进行认证
    subject.login(token);
}catch(JsonResultException e){
    this.handleFailResponse(response, e);
    return false;
}

结果如下
在这里插入图片描述
笑死,这返回了个啥,这边解析的Json数据,如果返回了字符串是不会展示,不管怎么样肯定不是我期待的Json数据格式。于是开始debug,debug路径DelegatingSubject->DefaultSecurityManager->AuthenticatingSecurityManager->AbstractAuthenticator,最终AbstractAuthenticator是处理异常的类,并且发现了一个好玩的东西
在这里插入图片描述
也就是最终在Filter中只能捕获到AuthenticationException,想要获取到自定义的异常,需要结构这个AuthenticationException,判断是否为自定义异常如果是那么进行你想要的处理,最终的版本就是上面的解决方案了。

修改之后,重新调用了一下接口

在这里插入图片描述
完美收官

结语

之后这个项目会开源,但是具体开源日期受笔者的开发进度影响比较大,随意点哈,暂时不给源码,因为写的太烂了。但是如果各位有更加占优的方式,欢迎在评论区评论

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值