@ResponseBodyAdvice @RequestBodyAdivce失效
背景
最近项目要有向外部提供服务的能力,但是考虑到数据安全问题,要对接口进行加解密;实现加解密的方案有很多,比如过滤器、拦截器、继承RequestResponseBodyMethodProcessor什么的,不过我最近正在了解@ResponseBodyAdvice @RequestBodyAdvice这俩注解,本着在实践中应用的目的,就准备使用这两个注解来实现加解密功能。
然而,配置好后,请求怎么都进不到这两个注解的类里。摸索了一天的时间,@RestController 和@ResponseBody 都加了,也确认已经扫描进容器中管理了,可就是无法生效。
原因
后来发现项目中之前有对所有的controller进行返回结果的统一包装,使用的是继承RequestResponseBodyMethodProcessor类来实现;
刚刚@ResponseBodyAdvice和@RequestBodyAdvice一直无法生效,就在RequestResponseBodyMethodProcessor这里面做了加密的动作,后来不经意间,把这个类在WebMvcConfigurer中导入的代码注掉了,惊奇的发现@ResponseBodyAdvice @RequestBodyAdvice这俩注解生效了。
所以初步定位 @ResponseBodyAdvice @RequestBodyAdvice 和RequestResponseBodyMethodProcessor 会冲突导致不生效。
解决
RequestResponseBodyMethodProcessor 里的逻辑抽取到@ResponseBodyAdvice里,本来这个也是对返回结果进行增强的,所以放到这里也非常合理。
同时扩展了加密的逻辑。
核心代码
@ControllerAdvice
public class ResponseProcessor implements ResponseBodyAdvice<Object> {private ObjectMapper om = new ObjectMapper();@AutowiredEncryptProperties encryptProperties;@Overridepublic boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {return methodParameter.hasMethodAnnotation(Encrypt.class);}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {byte[] keyBytes = encryptProperties.getKey().getBytes();try {if(!methodParameter.hasMethodAnnotation(NoResponseWrapperAnnotation.class)){body = new ResponseWrapper<>(body);}body = AESUtils.encrypt(JSONObject.toJSONString(body),encryptProperties.getKey());} catch (Exception e) {e.printStackTrace();}return body;}
}
``````java
@ControllerAdvice
public class RequestProcessor extends RequestBodyAdviceAdapter {@Autowiredprivate EncryptProperties encryptProperties;@Overridepublic boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {return methodParameter.hasMethodAnnotation(Decrypt.class) || methodParameter.hasParameterAnnotation(Decrypt.class);}@Overridepublic HttpInputMessage beforeBodyRead(final HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {byte[] body = new byte[inputMessage.getBody().available()];inputMessage.getBody().read(body);try {String decrypt = AESUtils.decrypt(new String(body), encryptProperties.getKey());final ByteArrayInputStream bais = new ByteArrayInputStream(decrypt.getBytes());return new HttpInputMessage() {@Overridepublic InputStream getBody() throws IOException {return bais;}@Overridepublic HttpHeaders getHeaders() {return inputMessage.getHeaders();}};} catch (Exception e) {e.printStackTrace();}return super.beforeBodyRead(inputMessage, parameter, targetType, converterType);}
}
``````java
public class AESUtils {private static final String KEY_ALGORITHM = "AES";private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";//默认的加密算法public static String getKey(int len){if(len % 16 != 0){System.out.println("长度要为16的整数倍");return null;}char[] chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();char[] uuid = new char[len];if (len > 0) {for (int i = 0; i < len; i++) {int x = (int) (Math.random() * (len - 0 + 1) + 0);uuid[i] = chars[x % chars.length];}}return new String(uuid);}public static String byteToHexString(byte[] bytes){StringBuffer sb = new StringBuffer();for (int i = 0; i < bytes.length; i++) {String strHex=Integer.toHexString(bytes[i]);if(strHex.length() > 3){sb.append(strHex.substring(6));} else {if(strHex.length() < 2){sb.append("0" + strHex);} else {sb.append(strHex);}}}return sb.toString();}/*** AES 加密操作** @param content 待加密内容* @param key 加密密码* @return 返回Base64转码后的加密数据*/public static String encrypt(String content, String key) {try {Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);// 创建密码器byte[] byteContent = content.getBytes("utf-8");cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(key));// 初始化为加密模式的密码器byte[] result = cipher.doFinal(byteContent);// 加密return org.apache.commons.codec.binary.Base64.encodeBase64String(result);//通过Base64转码返回} catch (Exception ex) {ex.printStackTrace();}return null;}/*** AES 解密操作** @param content* @param key* @return*/public static String decrypt(String content, String key) {try {//实例化Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);//使用密钥初始化,设置为解密模式cipher.init(Cipher.DECRYPT_MODE, getSecretKey(key));//执行操作byte[] result = cipher.doFinal(org.apache.commons.codec.binary.Base64.decodeBase64(content));return new String(result, "utf-8");} catch (Exception ex) {ex.printStackTrace();}return null;}private static SecretKeySpec getSecretKey(final String key) throws UnsupportedEncodingException {//返回生成指定算法密钥生成器的 KeyGenerator 对象
// KeyGenerator kg = null;// kg = KeyGenerator.getInstance(KEY_ALGORITHM);
//
// //AES 要求密钥长度为 128
// kg.init(128, new SecureRandom(key.getBytes()));
//
// //生成一个密钥
// SecretKey secretKey = kg.generateKey();return new SecretKeySpec(Arrays.copyOf(key.getBytes("utf-8"), 16), KEY_ALGORITHM);// 转换为AES专用密钥}
}
```相关文章:
@ResponseBodyAdvice @RequestBodyAdivce失效
背景 最近项目要有向外部提供服务的能力,但是考虑到数据安全问题,要对接口进行加解密;实现加解密的方案有很多,比如过滤器、拦截器、继承RequestResponseBodyMethodProcessor什么的,不过我最近正在了解ResponseBodyAd…...
【c#】Quartz开源任务调度框架学习及练习Demo
Quartz开源任务调度框架学习及练习Demo 1、定义、作用 2、原理 3、使用步骤 4、使用场景 5、Demo代码参考示例 6、注意事项 7、一些Trigger属性说明 1、定义、作用 Quartz是一个开源的任务调度框架,作用是支持开发人员可以定时处理业务,比如定时…...
spring cloud Eureka集群模式搭建(IDEA中运行)
spring cloud Eureka集群模式搭建(IDEA中运行) 新建springboot 工程工程整体目录配置文件IDEA中部署以jar包形式启动总结 新建springboot 工程 新建一个springboot 工程,命名为:eureka_server。 其中pom.xml文件为: …...
数据迁移一致性测试探索与实践
背景 量级庞大的日志通过mysql不足以支撑业务需求,以前通过任务调度定时跑批从mysql同步到hive存储,这种方式时效性为T1,也就是说今天的日志,明天才能同步到hive,总而言之时效性不高。为了提高时效性,改为…...
---图的遍历和最小生成树
广度优先遍历 --- 针对的是顶点遍历 深度优先遍历 如果给的图不是连通图?以某个点为起点就没有遍历完成。那么怎么保证遍历完剩下的点呢?? 在标记数组当中找没有遍历过的点,在进行遍历 最小生成树 生成树:一个连通…...
中文编程工具开发语言编程案例:会员VIP管理系统软件实例
中文编程工具开发语言编程案例:会员VIP管理系统软件实例 中文编程工具开发语言编程案例:会员VIP管理系统软件实例。 软件功能: 1、系统设置:参数设定,账号及权限设置,系统初始化,卡类型设置&a…...
Hive用户中文使用手册系列(四)
Python Client 在github 上上可以使用 Python client 驱动程序。有关安装说明,请参阅设置 HiveServer2:Python Client 驱动程序。 Ruby Client 一个 Ruby client 驱动程序在https://github.com/forward3d/rbhive的 github 上可用。 与 SQuirrel SQL …...
高级深入--day37
手机App抓包爬虫 1. items.py class DouyuspiderItem(scrapy.Item):name scrapy.Field()# 存储照片的名字imagesUrls scrapy.Field()# 照片的url路径imagesPath scrapy.Field()# 照片保存在本地的路径2. spiders/douyu.py import scrapy import json from douyuSpider.it…...
Web自动化测试:测试用例断言!
运行测试用例时,需要判断用例是否执行成功,此时需要有一个我们期望的结果来进行验证。这里unittest中,如果一个case执行的过程中报错,或者我们判断结果不符合期望,就会判定此条用例执行失败,判断的条件主要…...
基于SSM的培训学校教学管理平台的设计与实现
末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:Vue 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目:是 目录…...
2019年亚太杯APMCM数学建模大赛A题基于图像分析的二氧化硅熔化表示模型求解全过程文档及程序
2019年亚太杯APMCM数学建模大赛 A题 基于图像分析的二氧化硅熔化表示模型 原题再现 铁尾矿的主要成分是二氧化硅,而二氧化硅是铁尾矿成分中最难熔化的部分。因此,铁尾矿的熔融行为可以用二氧化硅的熔融行为来表示。然而,高温熔池的温度超过…...
C++之继承<2>【详解】
C之继承<2>【详解】 1. 派生类的默认成员函数1.1 1. 构造成员函数1.2 拷贝复制1.3 构造函数和析构函数的执行顺序 2. 继承和友元3. 继承与静态成员 1. 派生类的默认成员函数 1.1 1. 构造成员函数 派生类的构造函数必须调用基类的构造函数初始化基类的那…...
解决Kafka新消费者组导致重复消费的问题
问题描述:在使用Kafka时,当我们向新的消费者组中添加消费者时,可能会遇到重复消费的问题。本文将介绍一些解决这个问题的方法,帮助开发者更好地处理Kafka中的消费者组和消费偏移量。 Kafka是一个强大的分布式消息队列系统…...
公允价值会计(fair-value accounting)
《公允价值会计》是2008年经济科学出版社出版图书。 公允价值会计又称市值会计,是指以市场价值或未来现金流量的现值作为资产和负债的主要计量属性的会计模式。随着现代交通和通讯技术的发展,在工业社会中相互分割的市场正在走向世界一体化,…...
【java调取第三方接口,获取数据并保存至数据库】
java调取第三方接口,获取数据并保存至数据库 Overridepublic void doPost() {// 创建Httpclient对象CloseableHttpClient httpClient HttpClients.createDefault();CloseableHttpResponse response null;String resultString "";String url "htt…...
图论01-【无权无向】-图的基本表示-邻接矩阵/邻接表
文章目录 1. 代码仓库2. 图的基本表示的比较3. 邻接矩阵:Array和TreeSet3.1 图示3.2 Array主要代码解析3.3 测试输出3.4 使用TreeSet的代码 4. 邻接表:LinkedList4.1 图示4.2 LinkedList主要代码解析4.3 测试输出 5. 完整代码5.1 邻接表 - Array5.2 邻接…...
Bootstrap的列表组相关知识
目录 01-列表组的相关基础知识02-一个简单的列表组示例03-激活或禁用列表组的一行或多行04-设置列表项的颜色05-给列表项添加徽章 01-列表组的相关基础知识 Bootstrap的list-group是一个用于创建列表组件的CSS类,通常用于显示一个项目列表,如导航菜单或…...
Linux简单安装ffmpeg 实现用PHP压缩音频
一、下载安装 1、官方下载地址:Download FFmpeg 2、下载完上传到服务器然 然后解压就算安装完成了 tar -xf ffmpeg-git-amd64-static.tar.xz 3、然后配置一下全局变量(当然也可以不用配置 使用的时候带上文件路径就行) cd /usr/bin ln -s…...
Vue解决 npm -v 报错(一)
报错内容: npm WARN config global --global, --local are deprecated. Use --locationglobal instead. 解决方案: 代码: prefix -g 替换为: prefix --locationglobal 原创作者:吴小糖 创作时间:2023.1…...
IP地址是如何定位的
IP地址定位原理和方法 在互联网时代,了解设备或用户的地理位置对于各种应用和服务至关重要,从广告定向到网络安全。IP地址定位是一种常用的方法,允许确定IP地址背后的实际地理位置。本文将介绍IP地址定位的原理和方法。 IP地址基础…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...
Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...
