Spring Boot 处理过滤器(filter )中抛出的异常

前言:

在改造老项目登录功能的时候,使用了过滤器对 token 进行有效性验证,验证通过继续进行业务请求,验证不通过则抛出校验异常。

过程:

技术方案拟定后,就着手开始改造,一切都很顺畅,可是在异常场景模拟的时候,怎么也得不到想要的异常 code,我在过滤器的校验中明明是抛出了异常,为什么没有得到想要的结果呢?

过滤器代码如下:

@Slf4j
//@WebFilter(filterName = "myFilter", urlPatterns = "/*")
public class MyAuthenticationFilter implements Filter {//不拦截的 URLprivate final static String EXCLUDES_URI = "/api/workflow/*";@Overridepublic void init(FilterConfig filterConfig) throws ServletException {Filter.super.init(filterConfig);}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//为了方便获取 header 信息 对 HttpServletRequest 进行强转HttpServletRequest request = (HttpServletRequest) servletRequest;String requestUri = request.getRequestURI();//需要忽略的 url 地址Pattern pattern = Pattern.compile(EXCLUDES_URI);//是否放行boolean isExclude = pattern.matcher(requestUri).find();//用户信息UserInfoVO userInfoVO = null;if (!isExclude) {//校验认证信息userInfoVO = validateAuthorization(request);if (ObjectUtil.isNull(userInfoVO)) {//校验认证信息 失败 可能解析 token 异常 可能没有解析到正确的工号throw new AuthorizationValidationException(ResultCode.CAS_AUTHORIZATION);}}//设置用户信息UserContextHolder.setUser(userInfoVO);filterChain.doFilter(servletRequest, servletResponse);}@Overridepublic void destroy() {//将ThreadLocal数据清空UserContextHolder.remove();Filter.super.destroy();}/*** @Description: 校验 Authorization* @Date: 2024/4/3 10:25*/public UserInfoVO validateAuthorization(HttpServletRequest request) {//获取 AuthorizationString authorization = request.getHeader(CommConstant.AUTHORIZATION);if (StringUtils.isBlank(authorization)) {StringBuffer requestUrl = request.getRequestURL();log.info("Authorization 为空的请求url:{}", requestUrl);//Authorization 为空 没有登录return null;}//过滤器是 servlet 规范中定义的 不归 spring 容器管理  无法直接注入 spring 中的 bean 直接注入会为 null//只能够自己去 容器中获取 beanApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());assert context != null;AuthServiceImpl iAuthService = (AuthServiceImpl) context.getBean("authServiceImpl");//检验 authorizationreturn iAuthService.validateToken(authorization);}
}

配置过滤器代码:

package com.zt.zteam.main.configurer;import com.zt.zteam.main.filter.MyAuthenticationFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MyAuthenticationFilterConfig {@Bean("myFilterAuthentication")public FilterRegistrationBean<MyAuthenticationFilter> filterAuthenticationRegistration() {//设置过滤器FilterRegistrationBean<MyAuthenticationFilter> registration = new FilterRegistrationBean<>();registration.setFilter(new MyAuthenticationFilter());//设置过滤器优先级 数字越小优先级越高registration.setOrder(-1);return registration;}}

配置过滤方式二:

过滤器类上加 @WebFilter(filterName = “myFilter”, urlPatterns = “/*”) 注解,同时在启动类上加 @ServletComponentScan({“com.xxx.xxx.xxx.filter”}) 注解。

全局异常处理器代码如下:

@RestControllerAdvice(basePackages = "com.my.study")
//@Order(Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class GlobalExceptionHandler {/*** 交易异常*/@ExceptionHandler(ValidateException.class)public Result<?> validateExceptionHandler(HttpServletRequest request, ValidateException e) {log.error("校验异常,方法:{}", request.getRequestURI(), e);return ResultGenerator.genResult(e.getCode(), e.getMessage());}/*** 处理业务异常*/@ExceptionHandler(ServiceException.class)public Result<?> bizExceptionHandler(HttpServletRequest request, ServiceException e) {log.error("业务异常,方法:{}", request.getRequestURI(), e);return ResultGenerator.genResult(e.getCode(), e.getMessage());}/*** 处理空指针异常*/@ExceptionHandler(NullPointerException.class)public Result<?> npeHandler(HttpServletRequest request, NullPointerException e) {log.error("空指针异常,方法:{}", request.getRequestURI(), e);return ResultGenerator.genFailResult(e.getMessage());}/*** 处理其他异常*/@ExceptionHandler(Exception.class)public Result<?> exceptionHandler(HttpServletRequest request, Exception e) {log.error("其它异常,方法:{}", request.getRequestURI(), e);Result<?> result = new Result<>();result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage(e.getMessage() + "--接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员");return result;}@ExceptionHandler({BusinessException.class})public Result<?> businessException(HttpServletRequest request, Exception e) {log.error("业务处理异常信息,方法:{},异常信息:" ,request.getRequestURI(), e);Result<?> result = new Result<>();result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage(e.getMessage());return result;}/*** 这个是valid注解校验参数时,校验不通过的异常*/@ExceptionHandler(MethodArgumentNotValidException.class)public Result<?> validateMethodExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException e) {log.error("请求参数异常,方法:{}", request.getRequestURI(), e);Result<?> result = new Result<>();result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage(e.getBindingResult().getFieldErrors().get(0).getDefaultMessage());return result;}/*** token 认证验证*/@ExceptionHandler(AuthorizationValidationException.class)public Result<?> authorizationValidationExceptionHandler(HttpServletRequest request, Exception e) {log.error("CAS 认证异常,方法:{}", request.getRequestURI(), e);Result<?> result = new Result<>();result.setCode(ResultCode.UNAUTHORIZED).setMessage(e.getMessage() + "--接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员");return result;}}

测试结果:

{"timestamp": "2024-04-16T02:04:48.091+00:00","status": 500,"error": "Internal Server Error","message": "","path": "/api/logout"
}

没有得到我们预期的 code:401。

问题分析:

看起来代码显示的抛出了异常,也设置了全局异常处理器,但是并没有返回想要的异常状态码,至此感觉走到了死胡同,此时想到了老办法,debug 调试,经过多次 debug 调试,发现全局异常处理器没有拦截到任何异常,这就很能说明问题了,也就是全局异常处理器,根本捕获不到过滤器 filter 抛出的异常,那怎么办呢?我们知道全局异常过滤器是一定可以捕获到 Controller 的异常的,此时灵机一动,当出现异常后,在过滤器 filter 中使用 try catch 自己处理,然后使用 forward 转发请求到指定 Controller 不就可以了吗,方案有了,着手开始测试。

注意:@ControllerAdvice 注解只处理经过 Controller 的异常,不经过 Controller 的异常 @ControllerAdvice 注解不进行处理。

调整后的过滤器 filter 代码如下:

@Slf4j
public class MyAuthenticationFilter implements Filter {//不拦截的 URLprivate final static String EXCLUDES_URI = "/api/workflow/*";@Overridepublic void init(FilterConfig filterConfig) throws ServletException {Filter.super.init(filterConfig);}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//为了方便获取 header 信息 对 HttpServletRequest 进行强转HttpServletRequest request = (HttpServletRequest) servletRequest;String requestUri = request.getRequestURI();//需要忽略的 url 地址Pattern pattern = Pattern.compile(EXCLUDES_URI);//是否放行boolean isExclude = pattern.matcher(requestUri).find();//用户信息UserInfoVO userInfoVO = null;try {if (!isExclude) {//校验认证信息userInfoVO = validateAuthorization(request);if (ObjectUtil.isNull(userInfoVO)) {//校验认证信息 失败 可能解析 token 异常 可能没有解析到正确的工号throw new AuthorizationValidationException(ResultCode.CAS_AUTHORIZATION);}}//设置用户信息UserContextHolder.setUser(userInfoVO);filterChain.doFilter(servletRequest, servletResponse);} catch (AuthorizationValidationException e) {request.setAttribute(CommConstant.FILTER_ERROR, e);request.getRequestDispatcher(CommConstant.FILTER_ERROR_PATH).forward(request, servletResponse);} catch (IOException e) {e.printStackTrace();} catch (ServletException e) {e.printStackTrace();}}@Overridepublic void destroy() {//将ThreadLocal数据清空UserContextHolder.remove();Filter.super.destroy();}/*** @Description: 校验 Authorization* @Date: 2024/4/3 10:25*/public UserInfoVO validateAuthorization(HttpServletRequest request) {//获取 AuthorizationString authorization = request.getHeader(CommConstant.AUTHORIZATION);if (StringUtils.isBlank(authorization)) {StringBuffer requestUrl = request.getRequestURL();log.info("Authorization 为空的请求url:{}", requestUrl);//Authorization 为空 没有登录return null;}//过滤器是 servlet 规范中定义的 不归 spring 容器管理  无法直接注入 spring 中的 bean 直接注入会为 null//只能够自己去 容器中获取 beanApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());assert context != null;AuthServiceImpl iAuthService = (AuthServiceImpl) context.getBean("authServiceImpl");//检验 authorizationreturn iAuthService.validateToken(authorization);}
}

请求转发的关键代码:

//filterErrorpublic final static String FILTER_ERROR = "filterError";//filterError pathpublic final static String FILTER_ERROR_PATH = "/throw-error";//设置异常信息request.setAttribute(CommConstant.FILTER_ERROR, e);//转发request.getRequestDispatcher(CommConstant.FILTER_ERROR_PATH).forward(request, servletResponse);

异常处理 Controller 代码:

@Slf4j
@RestController
public class FilterExceptionController {@ApiOperation(value = "过滤器异常处理", produces = "application/json")@RequestMapping(CommConstant.FILTER_ERROR_PATH)public Result<String> testRedis(HttpServletRequest request) {Object attribute = request.getAttribute(CommConstant.FILTER_ERROR);if(attribute instanceof AuthorizationValidationException){throw new AuthorizationValidationException(ResultCode.CAS_AUTHORIZATION);}throw new BusinessException("业务异常");}
}

测试结果:

在这里插入图片描述
总结:

通过请求转发的方式,我们解决了过滤器 filter 异常无法捕获的问题,在转发的过程中我们尽量使用 request.getRequestDispatcher(“/path”).forward(request, response) 这种方式,此方式只会在服务端内部转发,客户端地址不会发生任何改变,如果使用response.sendRedirect(“/path”) 进行请求转发,客户端地址会发生改变。

在 Spring 应用中我们不建议优先使用过滤器 filter,建议优先使用拦截器 Interceptor,本文只是分享过滤器 filter 中的异常处理方式,希望帮助到有需要的伙伴们。

过滤器和拦截器的区别传送门:
Spring 拦截器实现请求拦截与参数处理【拦截器(Interceptor)和过滤器(Filter)的区别】

如有错误的地方欢迎指出纠正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/311870.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大数据平台搭建2024(二)

二&#xff1a;Hive安装 只在node01上操作 1 安装MySQL 8.0 最小化安装需要安装这个 yum install -y wget1-1 下载MySQL的yum源 wget http://dev.mysql.com/get/mysql80-community-release-el7-7.noarch.rpm检查是否安装成功 rpm -qpl mysql80-community-release-el7-7.n…

【YOLO系列PR、F1绘图】更改v5、v7、v8(附v8训练、验证方式),实现调用val.py或者test.py后生成pr.csv,然后再整合绘制到一张图上(使用matplotlib绘制)

目录 1. 前提 效果图2. 更改步骤2.1 得到PR_curve.csv和F1_curve.csv2.1.1 YOLOv7的更改2.1.1.1 得到PR_curve.csv2.2.1.2 得到F1_curve.csv 2.1.2 YOLOv5的更改&#xff08;v6.1版本&#xff09;2.1.3 YOLOv8的更改&#xff08;附训练、验证方式&#xff09; 2.2 绘制PR曲线 …

牛客-环形链表的约瑟夫问题

目录 题目 描述 示例1 示例2 图解 代码(解析在注释中) 题目 描述 编号为 1 到 n 的 n 个人围成一圈。从编号为 1 的人开始报数&#xff0c;报到 m 的人离开。 下一个人继续从 1 开始报数。 n-1 轮结束以后&#xff0c;只剩下一个人&#xff0c;问最后留下的这个人编号…

zabbix 自动发现与自动注册 部署 zabbix 代理服务器

zabbix 自动发现&#xff08;对于 agent2 是被动模式&#xff09; zabbix server 主动的去发现所有的客户端&#xff0c;然后将客户端的信息登记在服务端上。 缺点是如果定义的网段中的主机数量多&#xff0c;zabbix server 登记耗时较久&#xff0c;且压力会较大。1.确保客户端…

Qt | 事件第二节

Qt | 事件第一节书接上回 四、事件的接受和忽略 1、事件可以被接受或忽略,被接受的事件不会再传递给其他对象,被忽略的事件会被传递给其他对象处理,或者该事件被丢弃(即没有对象处理该事件) 2、使用 QEvent::accept()函数表示接受一个事件,使用 QEvent::ignore()函数表示…

认识异常(1)

❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&a…

OpenHarmony南向开发实例:【游戏手柄】

介绍 基于TS扩展的声明式开发范式编程语言&#xff0c;以及OpenHarmony的分布式能力实现的一个手柄游戏。 完成本篇Codelab需要两台开发板&#xff0c;一台开发板作为游戏端&#xff0c;一台开发板作为手柄端&#xff0c;实现如下功能&#xff1a; 游戏端呈现飞机移动、发射…

python生成二维码

要在Python中生成二维码&#xff0c;可以使用第三方库qrcode。首先&#xff0c;确保已经安装了qrcode库&#xff1a; pip install qrcode然后&#xff0c;使用以下代码生成二维码&#xff1a; import qrcodedata "https://mp.csdn.net/mp_blog/creation/editor?spm100…

项目7-音乐播放器2(上传音乐+查询音乐+拦截器)

0.加入拦截器 之后就不用对用户是否登录进行判断了 0.1 定义拦截器 0.2 注册拦截器 生效 1.上传音乐的接口设计 请求&#xff1a; { post, /music/upload {singer&#xff0c;MultipartFile file}&#xff0c; } 响应&#xff1a; { "status": 0, "message&…

Promise简单概述

一. Promise是什么&#xff1f; 理解 1.抽象表达&#xff1a; Promise是一门新的技术(ES6规范) Promise是JS中进行异步编程的新解决方案(旧方案是单纯使用回调函数) 异步编程&#xff1a;包括fs文件操作&#xff0c;数据库操作(Mysql)&#xff0c;AJAX&#xff0c;定时器 2.具…

RK3568笔记二十二:基于TACO的垃圾检测和识别

若该文为原创文章&#xff0c;转载请注明原文出处。 基于TACO数据集&#xff0c;使用YOLOv8分割模型进行垃圾检测和识别&#xff0c;并在ATK-RK3568上部署运行。 一、环境 1、测试训练环境&#xff1a;AutoDL. 2、平台&#xff1a;rk3568 3、开发板: ATK-RK3568正点原子板子…

大创项目推荐 深度学习YOLOv5车辆颜色识别检测 - python opencv

文章目录 1 前言2 实现效果3 CNN卷积神经网络4 Yolov56 数据集处理及模型训练5 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习YOLOv5车辆颜色识别检测 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0…

前端开发框架BootStrap

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl BootStrap概述 Bootstrap是一个开源的前端框架&#xff0c;它由Twitter的设计师和开发者创建并维护。Bootstrap提供了许多现成的Web组件&#xff0c;可帮助开发者快速设计和…

36、二叉树-二叉树的中序遍历

思路&#xff1a; 二叉树的遍历可以有 前序&#xff0c;中序&#xff0c;后序&#xff0c;层序遍历。 前序&#xff1a;头左右中序&#xff1a;左头右后序&#xff1a;左右头层序:从左往右依次遍历 实现方式&#xff1a; 递归通过栈结构便于回溯 代码如下&#xff1a; c…

软件开发安全设计方案

2.1.应用系统架构安全设计要求 2.2.应用系统软件功能安全设计要求 2.3.应用系统存储安全设计要求 2.4.应用系统通讯安全设计要求 2.5.应用系统数据库安全设计要求 2.6.应用系统数据安全设计要求 软件开发全资料获取&#xff1a;软件开发全套资料_软件开发资料-CSDN博客https://…

【C++学习】C++IO流

这里写目录标题 &#x1f680;C语言的输入与输出&#x1f680;什么是流&#x1f680;CIO流&#x1f680;C标准IO流&#x1f680;C文件IO流 &#x1f680;C语言的输入与输出 C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()。 scanf(): 从标准输入设备(键盘)读取…

Go Plugin:动态模块的加载与问题解析_go语言加载动态库的工具(1)

先自我介绍一下&#xff0c;小编浙江大学毕业&#xff0c;去过华为、字节跳动等大厂&#xff0c;目前阿里P7 深知大多数程序员&#xff0c;想要提升技能&#xff0c;往往是自己摸索成长&#xff0c;但自己不成体系的自学效果低效又漫长&#xff0c;而且极易碰到天花板技术停滞…

flutter书架形式格口的动态创建(行、列数,是否全选的配置)

根据传入的行列数创建不同格口数量的书架 左图&#xff1a;5行3列、右图&#xff1a;3行3列 代码 import package:jade/bean/experienceStation/ExpCellSpecsBean.dart; import package:jade/configs/PathConfig.dart; import package:jade/utils/DialogUtils.dart; import p…

springboot整合dubbo实现RPC服务远程调用

一、dubbo简介 1.什么是dubbo Apache Dubbo是一款微服务开发框架&#xff0c;他提供了RPC通信与微服务治理两大关键能力。有着远程发现与通信的能力&#xff0c;可以实现服务注册、负载均衡、流量调度等服务治理诉求。 2.dubbo基本工作原理 Contaniner:容器Provider&#xf…

Linux内核之WRITE_ONCE用法实例(四十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…