- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
客户端、服务端网络通信,为了安全,会对报文数据进行加解密操作.
在SpringBoot项目中,最好使用参考AOP思想,加解密与Controller业务逻辑解耦,互不影响.
以解密为例:需要在request请求到达Controller之前进行拦截,获取请求body中的密文并对其进行解密,然后把解密后的明文重新设置到request的body上.
如图所示,在Request对象进入Controller之前,可使用Filter过滤器和Interceptor拦截器进行拦截.
过滤器、拦截器主要差异:
1、过滤器来自于 Servlet;而拦截器来自于 Spring 框架;
2、触发时机不同:请求的执行顺序是:请求进入容器 > 进入过滤器 > 进入 Servlet > 进入拦截器 > 执行控制器(Controller)
3、过滤器是基于方法回调实现的;拦截器是基于动态代理(底层是反射)实现的;
4、过滤器是 Servlet 规范中定义的,所以过滤器要依赖 Servlet 容器,它只能用在 Web 项目中;拦截器是 Spring 中的一个组件,因此拦截器既可以用在 Web 项目中,同时还可以用在 Application 或 Swing 程序中;
5、过滤器通常是用来实现通用功能过滤的,比如:敏感词过滤、字符集编码设置、响应数据压缩等功能;拦截器更接近业务系统,所以拦截器主要用来实现项目中的业务判断的,比如:登录判断、权限判断、日志记录等业务;
对于我们当前应用场景来说,区别就是过滤器更适用于修改request body.
修改请求,会有两个问题: 1、请求体的输入流被读取,它就不能再被其他组件读取,因为输入流只能被标记、重置,并且在读取后会被消耗。 2、HttpServletRequest对象的body数据只能get,不能set,不能再次赋值。而咱们的需求是需要给HttpServletRequest body解密并重新赋值。 基于以上两个问题,咱们需要定义一个HttpServletRequest实现类,增加赋值方法,来满足我们的需求。 CustomHttpServletRequestWrapper.java 。
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
/**
* 自定义HttpServletRequestWrapper
* qxc
* 20240622
*/
public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {
private String body;
public CustomHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
InputStream inputStream = null;
try {
inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
} finally {
if (inputStream != null) {
try {
inputStream.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
body = stringBuilder.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
public String getBody() {
return this.body;
}
public void setBody(String body) {
this.body = body;
}
}
接下来,继续写Filter类,用于拦截请求,解析body, 解密报文,替换请求body数据。 RequestWrapperFilter.java 。
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.Objects;
/**
* 自定义Filter
* qxc
* 20240622
*/
@Slf4j
public class RequestWrapperFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
CustomHttpServletRequestWrapper customHttpServletRequestWrapper = null;
try {
HttpServletRequest req = (HttpServletRequest) request;
customHttpServletRequestWrapper = new CustomHttpServletRequestWrapper(req);
preHandle(customHttpServletRequestWrapper);
} catch (Exception e) {
log.warn("customHttpServletRequestWrapper Error:", e);
}
chain.doFilter((Objects.isNull(customHttpServletRequestWrapper) ? request : customHttpServletRequestWrapper), response);
}
public void preHandle(CustomHttpServletRequestWrapper request) throws Exception {
//仅当请求方法为POST时修改请求体
if (!request.getMethod().equalsIgnoreCase("POST")) {
return;
}
//读取原始请求体
StringBuilder originalBody = new StringBuilder();
String line;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()))) {
while ((line = reader.readLine()) != null) {
originalBody.append(line);
}
}
String bodyText = originalBody.toString();
//json字符串转成map集合
Map<String, String> map = getMap(bodyText);
//获取解密参数,解密数据
if (map != null && map.containsKey("time") && map.containsKey("data")) {
String time = map.get("time");
String key = "基于时间戳等参数生成密钥、此处请换成自己的密钥";
String data = map.get("data");
//解密数据
String decryptedData = Cipher.decrypt(key, data);
//为请求对象重新设置body
request.setBody(decryptedData);
}
}
private Map<String, String> getMap(String text) {
ObjectMapper objectMapper = new ObjectMapper();
try {
// 将JSON字符串转换为Map
Map<String, String> map = objectMapper.readValue(text, Map.class);
return map;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
AES加解密算法封装 Cipher.java 。
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
import javax.crypto.spec.SecretKeySpec;
/**
* 自定义AES加解密算法类
* qxc
* 20240622
*/
public class Cipher {
public static String encrypt(String key, String text){
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES");
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] encryptedBytes = cipher.doFinal(text.getBytes("UTF-8"));
String cipherText = base64Encode(encryptedBytes);
return cipherText;
}catch (Exception ex){
ex.printStackTrace();
return "";
}
}
public static String decrypt(String key, String cipherText) {
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES");
cipher.init(javax.crypto.Cipher.DECRYPT_MODE, secretKeySpec);
byte[] decryptedBytes = cipher.doFinal(base64Decode(cipherText));
return new String(decryptedBytes, StandardCharsets.UTF_8);
}catch (Exception ex){
ex.printStackTrace();
return "";
}
}
public static String getMd5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(input.getBytes());
byte[] digest = md.digest();
BigInteger bigInt = new BigInteger(1, digest);
String md5Hex = bigInt.toString(16);
while (md5Hex.length() < 32) {
md5Hex = "0" + md5Hex;
}
return md5Hex;
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
public static String base64Encode(byte[] bytes) {
if (bytes != null && bytes.length > 0) {
return Base64.getEncoder().encodeToString(bytes);
}
return "";
}
public static byte[] base64Decode(String base64Str) {
if (base64Str != null && base64Str.length() > 0) {
return Base64.getDecoder().decode(base64Str);
}
return new byte[]{};
}
}
最后,需要在WebMvcConfigurer中配置并使用RequestWrapperFilter 。
import com.qxc.server.encryption.RequestWrapperFilter;
import com.qxc.server.jwt.JwtInterceptor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
public class ServerApplication implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
@Bean
public FilterRegistrationBean servletRegistrationBean() {
RequestWrapperFilter userInfoFilter = new RequestWrapperFilter();
FilterRegistrationBean<RequestWrapperFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(userInfoFilter);
bean.setName("requestFilter");
bean.addUrlPatterns("/*");
bean.setOrder(Ordered.LOWEST_PRECEDENCE);
return bean;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
}
}
这样,就实现了请求报文的解密、数据替换,而且对Controller逻辑没有影响.
最后此篇关于SpringBoot过滤器更改Requestbody,并实现数据解密的文章就讲到这里了,如果你想了解更多关于SpringBoot过滤器更改Requestbody,并实现数据解密的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
SO亲爱的 friend 们: 2014 年 3 月 18 日。我正在处理一种情况,在使用 ng-repeat 时,数组内的元素(我从 Json 字符串中获取)更改了原始顺序。 需要明确的是,数组中的
有很多问题询问如何在 JavaScript 单击处理程序中更改 div 的类,例如,此处:Change Div style onclick .我理解得很好(只需更改 .className),并且它有效
我从access导入了一个数据库到mysql,但其中一个表的列名“股数”带有空格,但我尝试更改、替换甚至删除列名,但失败了。任何人都可以帮助解决这一问题 String UpdateQuary = "U
我正在做一个随机的学校元素。 目前,我有一个包含两个 CSS 的页面。一种用于正常 View ,一种用于残障人士 View 。 此页面还包括两个按钮,它们将更改使用的样式表。 function c
我需要使用 javascript 更改 HTML 元素中的文本,但我不知道该怎么做。 ¿有什么帮助吗? 我把它定义成这样: Text I want to change. 我正在尝试这样做: docum
我在它自己的文件 nav_bar.shtml 中有一个主导航栏,每个其他页面都包含该导航栏。这个菜单栏是一个 jQuery 菜单栏(ApyCom 是销售这些导航栏的公司的名称)。导航栏上的元素如何确定
我正在摆弄我的代码,并开始想知道这个变化是否来自: if(array[index] == 0) 对此: if(!array[index] != 0) 可能会影响任何代码,或者它只是做同样的事情而我不需
我一直在想办法调整控制台窗口的大小。这是我正在使用的函数的代码: #include #include #define WIDTH 70 #define HEIGHT 35 HANDLE wHnd;
我有很多情况会导致相同的消息框警报。 有没有比做几个 if 语句更简单/更好的解决方案? PRODUCTS BOX1 BOX2 BOX3
我有一个包含这些元素的 XELEMENT B Bob Petier 19310227 1 我想像这样转换前缀。 B Bob Pet
我使用 MySQL 5.6 遇到了这种情况: 此查询有效并返回预期结果: select * from some_table where a = 'b' and metadata->>"$.countr
我想知道是否有人知道可以检测 R 中日期列格式的任何中断的包或函数,即检测日期向量格式更改的位置,例如: 11/2/90 12/2/90 . . . 15/Feb/1990 16/Feb/1990 .
我希望能够在小部件显示后更改 GtkButton 的标签 char *ButtonStance == "Connect"; GtkWidget *EntryButton = gtk_button_ne
我正在使用 Altera DE2 FPGA 开发板并尝试使用 SD 卡端口和音频线路输出。我正在使用 VHDL 和 C 进行编程,但由于缺乏经验/知识,我在 C 部分遇到了困难。 目前,我可以从 SD
注意到这个链接后: http://www.newscientist.com/blogs/nstv/2010/12/best-videos-of-2010-progress-bar-illusion.h
我想知道在某些情况下,即使剧本任务已成功执行并且 ok=2,ansible 也会显示“changed=0”。使用 Rest API 和 uri 模块时会发生这种情况。我试图找到解释但没有成功。谁能告诉
这个问题已经有答案了: 已关闭12 年前。 Possible Duplicate: add buttons to push notification alert 是否可以在远程通知显示的警报框中指定有
当您的 TabBarController 中有超过 5 个 View Controller 时,系统会自动为您设置一个“更多” View 。是否可以更改此 View 中导航栏的颜色以匹配我正在使用的颜
如何更改.AndroidStudioBeta文件夹的位置,默认情况下,该文件夹位于Windows中的\ .. \ User \ .AndroidStudioBeta,而不会破坏任何内容? /编辑: 找
我目前正在尝试将更具功能性的编程风格应用于涉及低级(基于 LWJGL)GUI 开发的项目。显然,在这种情况下,需要携带很多状态,这在当前版本中是可变的。我的目标是最终拥有一个完全不可变的状态,以避免状
我是一名优秀的程序员,十分优秀!