@ResponseBodyAdvice @RequestBodyAdivce失效

news/2024/2/28 0:49:38

背景

最近项目要有向外部提供服务的能力,但是考虑到数据安全问题,要对接口进行加解密;实现加解密的方案有很多,比如过滤器、拦截器、继承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专用密钥}
}
```

http://www.ppmy.cn/news/1165783.html

相关文章

【Python第三方包】解析和生成二维码(pyqrcode包)

文章目录 前言一、安装pyqrcode包二、生成二维码2.1 二维码生成基础使用2.2 自定义二维码样式颜色设置错误纠正级别尺寸设置三、解析二维码总结前言 在现代信息时代,二维码(QR码)已经成为了快速传递信息的常见方式。Python提供了多种第三方包,用于生成和解析二维码。其中,…

【Javascript】声明变量

目录 1.声明和赋值结合 2.声明和赋值分开 3.console.log() 控制台打印 1.打印单个变量 2.打印多个变量 ​编辑 3.打印变量类型 ​编辑 注意&#xff1a; ​编辑 4.直接打印未声明的变量会报错 5.变量提升 变量提升的影响 1.声明和赋值结合 2.声明和赋值分开 3.cons…

C++设计模式_09_Abstract Factory 抽象工厂

与上篇介绍的Factory Method工厂方法模式一样&#xff0c;Abstract Factory 抽象工厂模式也属于典型的“对象创建模式”模式&#xff0c;解决的问题也极其相似&#xff0c;在理解了Factory Method工厂方法模式的基础上再去理解Abstract Factory 抽象工厂模式就会变得更加容易。…

获取电商订单接口需要哪些资质呢?

获取电商订单接口需要哪些资质呢&#xff1f;请看下面讲解 获取电商订单接口有多种方式&#xff0c;如果通过电商平台的开放平台获取电商订单接口&#xff0c;在入驻开放平台时便需要一定的资质。 那就以淘宝开放平台为例&#xff0c;有不同的接入模式可供选择&#xff0c;分…

C# 给List编个序号

给List编个号 int[] numbers { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };int i 0;var q (from n in numbersselect (index:i,number:n)).ToList();foreach (var v in q) {Console.WriteLine($"i {v.index}, v {v.number}"); }

初识Java 14-2 测试

目录 测试驱动开发&#xff08;TDD&#xff09; 日志 调试 使用JDB进行调试 基准测试 微基准测试 Java微基准测试工具&#xff08;JMH&#xff09; 分析和优化 重构 本笔记参考自&#xff1a; 《On Java 中文版》 测试驱动开发&#xff08;TDD&#xff09; 测试驱动开…

web前端基础CSS------美化页面“footer”部分

一&#xff0c;实验代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>关于我们</title><style type"text/css">#footer{margin: 10px 0px;background: #f5f5f5;border: top 1px solid #eee ;}#f…

旧版Mac如何装新系统

macOS Ventura 最低系统需要&#xff0c;17年序列电脑。老电脑15年的&#xff0c;无法安装新系统。使用方法直接采用大佬方法 一.在GitHub下载 OpenCore、Hackintool OpenCore 用来修改系统的机型&#xff0c;修改后可直接在软件更新中更新macOS Ventura。 Hackintool 用来生…

vue2升级到vue2.7

vue2升级到vue2.7 小小的改进,大大的提升 只需要简单修改,开发体验得到大大提升. 为什么要升级Vue2.7 不能拒绝的理由: 组合式 API(解决mixins问题:命名冲突,隐式依赖)单文件组件内的 <script setup>语法模板表达式中支持 ESNext 语法(可选链:?.、空值合并:??)单文…

实用API管理平台推荐:Apipost

在数字化时代&#xff0c;API已成为企业和开发者实现数据互通、应用集成的重要桥梁。然而&#xff0c;随着API数量的不断增加&#xff0c;API设计、调试、文档和测试等工作也变得越来越复杂。为了解决这一痛点&#xff0c;一款名为Apipost的API协同研发工具应运而生&#xff0c…

前端常见的数据类型有哪些?

在前端开发中,常见的数据类型包括: 1:字符串(String):表示文本数据,用引号(单引号或双引号)括起来,例如:“Hello, World!”。 创建字符串:let str = ‘Hello, World!’;获取字符串长度:let length = str.length;字符串拼接:let newStr = str1 + str2;2:数字(N…

ArcGIS笔记10_如何创建渔网?

本文目录 前言Step 1 确定渔网的精度单位Step 2 有底图时创建渔网的操作 前言 ArcGIS中的渔网是一个很好用的工具&#xff0c;它可以创建出规规整整的小格子&#xff0c;每个小格子都对应一个标注点&#xff0c;可以将原本散乱的数据规整化&#xff0c;如下图&#xff1a; Ste…

@Controller与@RestController

总结 Controller &#xff1a;定义一个控制器类. RequestMapping &#xff1a;给出外界访问方法的路径&#xff0c;或者说触发路径 &#xff0c;触发条件。 具体解析访问路径到某个方法上. ResponseBody &#xff1a;标记Controller类中的方法。把return的结果变成JSON对象…

centos 7.9 安装sshpass

1.作用 sshpass是一个用于非交互式SSH密码验证的实用程序。它可以用于自动输入密码以进行SSH登录&#xff0c;从而简化了自动化脚本和批处理作业中的SSH连接过程。 sshpass命令可以与ssh命令一起使用&#xff0c;通过在命令行中提供密码参数来执行远程命令。以下是一个示例命…

安装Sentinel

大家好今天来安装Sentinel . 安装Sentinel 下载 : 大家可以选择相应版本(最新版本1.8.6) 官网下载地址 : Release v1.8.6 alibaba/Sentinel GitHub 链接&#xff1a;Sentinel_免费高速下载|百度网盘-分享无限制 (baidu.com) 提取码&#xff1a;8eh9 运行 : 将jar包放到任…

基于B/S架构,包括PC后台管理端、APP移动端、可视化大屏端的智慧工地源码

智慧工地管理平台充分运用数字化技术&#xff0c;聚焦施工现场岗位一线&#xff0c;依托物联网、互联网、AI等技术&#xff0c;围绕施工现场管理的人、机、料、法、环五大维度&#xff0c;以及施工过程管理的进度、质量、安全三大体系为基础应用&#xff0c;实现全面高效的工程…

程序设计:C++ 一个可以放入共享内存的string模板

共享内存由于是多进程共享的&#xff0c;里面的数据不适合包含指针&#xff0c;因为共享内存在不同进程里的地址并不相同。尽管可以在连接共享内存时指定连接地址&#xff0c;但是&#xff0c;这样做限制太多&#xff1a; 不同硬件、系统这个地址可能不一样&#xff0c;没有通…

Simulink 最基础教程(一)

1.1基本概念 一个典型的Simulink模型大致如上图这样&#xff1a; 1&#xff09;模块 block&#xff1a;图中画圈的那些&#xff0c;每个模块可以完成一些特定的任务&#xff0c;类似MATLAB中函数的概念。软件提供了很多模块&#xff0c;当然也可以自定义新的模块 2&#xff0…

论文阅读之《Kindling the Darkness: A Practical Low-light Image Enhancer》

目录 摘要 介绍 已有方法回顾 普通方法 基于亮度的方法 基于深度学习的方法 基于图像去噪的方法 提出的方法 2.1 Layer Decomposition Net 2.2 Reflectance Restoration Net 2.3 Illumination Adjustment Net 实验结果 总结 Kindling the Darkness: A Practical L…

【系统架构设计】计算机公共基础知识: 1 嵌入式系统

目录 一 嵌入式系统 1 概述 2 嵌入式系统设计 (1)交叉开发环境
最新文章