- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章使用注解+RequestBodyAdvice实现http请求内容加解密方式由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
实现比较简单 。
1
2
3
4
5
6
|
@Target
({ElementType.METHOD})
@Retention
(RetentionPolicy.RUNTIME)
public
@interface
SecretAnnotation {
boolean
encode()
default
false
;
boolean
decode()
default
false
;
}
|
1
2
3
4
5
|
@PostMapping
(
"/preview"
)
@SecretAnnotation
(decode =
true
)
public
ResponseVO<ContractSignVO> previewContract(
@RequestBody
FillContractDTO fillContractDTO) {
return
contractSignService.previewContract(fillContractDTO);
}
|
请求数据由二进制流转为类对象数据,对于加密过的数据,需要在二进制流被处理之前进行解密,否则在转为类对象时会因为数据格式不匹配而报错.
因此使用RequestBodyAdvice的beforeBodyRead方法来处理.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
@Slf4j
@RestControllerAdvice
public
class
MyRequestControllerAdvice
implements
RequestBodyAdvice {
@Override
public
boolean
supports(MethodParameter methodParameter, Type type, Class<?
extends
HttpMessageConverter<?>> aClass) {
return
methodParameter.hasParameterAnnotation(RequestBody.
class
);
}
@Override
public
Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<?
extends
HttpMessageConverter<?>> aClass) {
return
o;
}
@Autowired
private
MySecretUtil mySecretUtil;
@Override
public
HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<?
extends
HttpMessageConverter<?>> aClass)
throws
IOException {
if
(methodParameter.getMethod().isAnnotationPresent(SecretAnnotation.
class
)) {
SecretAnnotation secretAnnotation = methodParameter.getMethod().getAnnotation(SecretAnnotation.
class
);
if
(secretAnnotation.decode()) {
return
new
HttpInputMessage() {
@Override
public
InputStream getBody()
throws
IOException {
List<String> appIdList = httpInputMessage.getHeaders().get(
"appId"
);
if
(appIdList.isEmpty()){
throw
new
RuntimeException(
"请求头缺少appID"
);
}
String appId = appIdList.get(
0
);
String bodyStr = IOUtils.toString(httpInputMessage.getBody(),
"utf-8"
);
bodyStr = mySecretUtil.decode(bodyStr,appId);
return
IOUtils.toInputStream(bodyStr,
"utf-8"
);
}
@Override
public
HttpHeaders getHeaders() {
return
httpInputMessage.getHeaders();
}
};
}
}
return
httpInputMessage;
}
@Override
public
Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<?
extends
HttpMessageConverter<?>> aClass) {
return
o;
}
}
|
mySecretUtil.decode(bodyStr,appId)的内容是,通过请求头中的AppID去数据库中查找对于的秘钥,之后进行解密,返回解密后的字符串.
再通过common.io包中提供的工具类IOUtils将字符串转为inputstream流,替换HttpInputMessage,返回一个body数据为解密后的二进制流的HttpInputMessage.
在实际项目中,我们常常需要在请求前后进行一些操作,比如:参数解密/返回结果加密,打印请求参数和返回结果的日志等。这些与业务无关的东西,我们不希望写在controller方法中,造成代码重复可读性变差。这里,我们讲讲使用@ControllerAdvice和RequestBodyAdvice、ResponseBodyAdvice来对请求前后进行处理(本质上就是AOP),来实现日志记录每一个请求的参数和返回结果.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
package
com.linkus.common.utils;
import
java.security.Key;
import
java.security.NoSuchAlgorithmException;
import
java.security.NoSuchProviderException;
import
java.security.Security;
import
javax.annotation.PostConstruct;
import
javax.crypto.Cipher;
import
javax.crypto.NoSuchPaddingException;
import
javax.crypto.spec.IvParameterSpec;
import
javax.crypto.spec.SecretKeySpec;
import
org.bouncycastle.jce.provider.BouncyCastleProvider;
import
org.bouncycastle.util.encoders.Hex;
import
org.springframework.beans.factory.annotation.Value;
import
org.springframework.stereotype.Component;
@Component
public
class
Aes {
/**
*
* @author ngh
* AES128 算法
*
* CBC 模式
*
* PKCS7Padding 填充模式
*
* CBC模式需要添加偏移量参数iv,必须16位
* 密钥 sessionKey,必须16位
*
* 介于java 不支持PKCS7Padding,只支持PKCS5Padding 但是PKCS7Padding 和 PKCS5Padding 没有什么区别
* 要实现在java端用PKCS7Padding填充,需要用到bouncycastle组件来实现
*/
private
String sessionKey=
"加解密密钥"
;
// 偏移量 16位
private
static
String iv=
"偏移量"
;
// 算法名称
final
String KEY_ALGORITHM =
"AES"
;
// 加解密算法/模式/填充方式
final
String algorithmStr =
"AES/CBC/PKCS7Padding"
;
// 加解密 密钥 16位
byte
[] ivByte;
byte
[] keybytes;
private
Key key;
private
Cipher cipher;
boolean
isInited =
false
;
public
void
init() {
// 如果密钥不足16位,那么就补足. 这个if 中的内容很重要
keybytes = iv.getBytes();
ivByte = iv.getBytes();
Security.addProvider(
new
BouncyCastleProvider());
// 转化成JAVA的密钥格式
key =
new
SecretKeySpec(keybytes, KEY_ALGORITHM);
try
{
// 初始化cipher
cipher = Cipher.getInstance(algorithmStr,
"BC"
);
}
catch
(NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch
(NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch
(NoSuchProviderException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 加密方法
*
* @param content
* 要加密的字符串
* 加密密钥
* @return
*/
public
String encrypt(String content) {
byte
[] encryptedText =
null
;
byte
[] contentByte = content.getBytes();
init();
try
{
cipher.init(Cipher.ENCRYPT_MODE, key,
new
IvParameterSpec(ivByte));
encryptedText = cipher.doFinal(contentByte);
}
catch
(Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return
new
String(Hex.encode(encryptedText));
}
/**
* 解密方法
*
* @param encryptedData
* 要解密的字符串
* 解密密钥
* @return
*/
public
String decrypt(String encryptedData) {
byte
[] encryptedText =
null
;
byte
[] encryptedDataByte = Hex.decode(encryptedData);
init();
try
{
cipher.init(Cipher.DECRYPT_MODE, key,
new
IvParameterSpec(ivByte));
encryptedText = cipher.doFinal(encryptedDataByte);
}
catch
(Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return
new
String(encryptedText);
}
public
static
void
main(String[] args) {
Aes aes =
new
Aes();
String a=
"{\n"
+
"\"distance\":\"1000\",\n"
+
"\"longitude\":\"28.206471\",\n"
+
"\"latitude\":\"112.941301\"\n"
+
"}"
;
//加密字符串
//String content = "孟飞快跑";
// System.out.println("加密前的:" + content);
// System.out.println("加密密钥:" + new String(keybytes));
// 加密方法
String enc = aes.encrypt(a);
System.out.println(
"加密后的内容:"
+ enc);
String dec=
""
;
// 解密方法
try
{
dec = aes.decrypt(enc);
}
catch
(Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(
"解密后的内容:"
+ dec);
}
}
|
前端页面传过来的是密文,我们需要在Controller获取请求之前对密文解密然后传给Controller 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
package
com.linkus.common.filter;
import
com.alibaba.fastjson.JSON;
import
com.linkus.common.constant.KPlatResponseCode;
import
com.linkus.common.exception.CustomException;
import
com.linkus.common.exception.JTransException;
import
com.linkus.common.service.util.MyHttpInputMessage;
import
com.linkus.common.utils.Aes;
import
com.linkus.common.utils.http.HttpHelper;
import
lombok.extern.slf4j.Slf4j;
import
org.slf4j.Logger;
import
org.slf4j.LoggerFactory;
import
org.springframework.core.MethodParameter;
import
org.springframework.http.HttpInputMessage;
import
org.springframework.http.converter.HttpMessageConverter;
import
org.springframework.lang.Nullable;
import
org.springframework.stereotype.Component;
import
org.springframework.web.bind.annotation.ControllerAdvice;
import
org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import
javax.servlet.http.HttpServletRequest;
import
java.io.*;
import
java.lang.reflect.Type;
/**
* 请求参数 解密操作
*
* @Author: Java碎碎念
* @Date: 2019/10/24 21:31
*
*/
@Component
//可以配置指定需要解密的包,支持多个
@ControllerAdvice
(basePackages = {
"com.linkus.project"
})
@Slf4j
public
class
DecryptRequestBodyAdvice
implements
RequestBodyAdvice {
Logger log = LoggerFactory.getLogger(getClass());
Aes aes=
new
Aes();
@Override
public
boolean
supports(MethodParameter methodParameter, Type targetType, Class<?
extends
HttpMessageConverter<?>> converterType) {
//true开启功能,false关闭这个功能
return
true
;
}
//在读取请求之前做处理
@Override
public
HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter, Type targetType, Class<?
extends
HttpMessageConverter<?>> selectedConverterType)
throws
IOException {
//获取请求数据
String string =
""
;
BufferedReader bufferedReader =
null
;
InputStream inputStream = inputMessage.getBody();
//这个request其实就是入参 可以从这里获取流
//入参放在HttpInputMessage里面 这个方法的返回值也是HttpInputMessage
try
{
string=getRequestBodyStr(inputStream,bufferedReader);
}
finally
{
if
(bufferedReader !=
null
) {
try
{
bufferedReader.close();
}
catch
(IOException ex) {
throw
ex;
}
}
}
/*****************进行解密start*******************/
String decode =
null
;
if
(HttpHelper.isEncrypted(inputMessage.getHeaders())){
try
{
// //解密操作
//Map<String,String> dataMap = (Map)body;
//log.info("接收到原始请求数据={}", string);
// inputData 为待加解密的数据源
//解密
decode= aes.decrypt(string);
//log.info("解密后数据={}",decode);
}
catch
(Exception e ) {
log.error(
"加解密错误:"
,e);
throw
new
CustomException(KPlatResponseCode.MSG_DECRYPT_TIMEOUT,KPlatResponseCode.CD_DECRYPT_TIMEOUT);
}
//把数据放到我们封装的对象中
}
else
{
decode = string;
}
// log.info("接收到请求数据={}", decode);
// log.info("接口请求地址{}",((HttpServletRequest)inputMessage).getRequestURI());
return
new
MyHttpInputMessage(inputMessage.getHeaders(),
new
ByteArrayInputStream(decode.getBytes(
"UTF-8"
)));
}
@Override
public
Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<?
extends
HttpMessageConverter<?>> converterType) {
return
body;
}
@Override
public
Object handleEmptyBody(
@Nullable
Object var1, HttpInputMessage var2, MethodParameter var3, Type var4, Class<?
extends
HttpMessageConverter<?>> var5) {
return
var1;
}
//自己写的方法,不是接口的方法,处理密文
public
String getRequestBodyStr( InputStream inputStream,BufferedReader bufferedReader)
throws
IOException {
StringBuilder stringBuilder =
new
StringBuilder();
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(
""
);
}
String string = stringBuilder.toString();
return
string;
}
}
|
将返给前端的响应加密,保证数据的安全性 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
package
com.linkus.common.filter;
import
com.alibaba.fastjson.JSON;
import
com.linkus.common.utils.Aes;
import
com.linkus.common.utils.DesUtil;
import
com.linkus.common.utils.http.HttpHelper;
import
io.swagger.models.auth.In;
import
lombok.experimental.Helper;
import
lombok.extern.slf4j.Slf4j;
import
org.slf4j.Logger;
import
org.slf4j.LoggerFactory;
import
org.springframework.core.MethodParameter;
import
org.springframework.http.MediaType;
import
org.springframework.http.converter.HttpMessageConverter;
import
org.springframework.http.server.ServerHttpRequest;
import
org.springframework.http.server.ServerHttpResponse;
import
org.springframework.stereotype.Component;
import
org.springframework.web.bind.annotation.ControllerAdvice;
import
org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import
java.lang.reflect.Field;
import
java.util.ArrayList;
import
java.util.HashMap;
import
java.util.List;
import
java.util.Map;
/**
* 请求参数 加密操作
*
* @Author: Java碎碎念
* @Date: 2019/10/24 21:31
*
*/
@Component
@ControllerAdvice
(basePackages = {
"com.linkus.project"
})
@Slf4j
public
class
EncryResponseBodyAdvice
implements
ResponseBodyAdvice<Object> {
Logger log = LoggerFactory.getLogger(getClass());
Aes aes=
new
Aes();
@Override
public
boolean
supports(MethodParameter returnType, Class<?
extends
HttpMessageConverter<?>> converterType) {
return
true
;
}
@Override
public
Object beforeBodyWrite(Object obj, MethodParameter returnType, MediaType selectedContentType,
Class<?
extends
HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest serverHttpRequest,
ServerHttpResponse serverHttpResponse) {
String returnStr =
""
;
Object retObj =
null
;
log.info(
"接口请求地址{}"
,serverHttpRequest.getURI());
//日志过滤
//retObj=infofilter.getInfoFilter(returnType,obj);
if
(HttpHelper.isEncrypted(serverHttpRequest.getHeaders())) {
try
{
//添加encry header,告诉前端数据已加密
//serverHttpResponse.getHeaders().add("infoe", "e=a");
//获取请求数据
String srcData = JSON.toJSONString(obj);
//加密
returnStr = aes.encrypt(srcData).replace(
"\r\n"
,
""
);
//log.info("原始数据={},加密后数据={}", obj, returnStr);
return
returnStr;
}
catch
(Exception e) {
log.error(
"异常!"
, e);
}
}
log.info(
"原始数据={}"
,JSON.toJSONString(obj));
return
obj;
}
}
|
以上为个人经验,希望能给大家一个参考,也希望大家多多支持我.
原文链接:https://blog.csdn.net/more_try/article/details/88682588 。
最后此篇关于使用注解+RequestBodyAdvice实现http请求内容加解密方式的文章就讲到这里了,如果你想了解更多关于使用注解+RequestBodyAdvice实现http请求内容加解密方式的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我在网上搜索但没有找到任何合适的文章解释如何使用 javascript 使用 WCF 服务,尤其是 WebScriptEndpoint。 任何人都可以对此给出任何指导吗? 谢谢 最佳答案 这是一篇关于
我正在编写一个将运行 Linux 命令的 C 程序,例如: cat/etc/passwd | grep 列表 |剪切-c 1-5 我没有任何结果 *这里 parent 等待第一个 child (chi
所以我正在尝试处理文件上传,然后将该文件作为二进制文件存储到数据库中。在我存储它之后,我尝试在给定的 URL 上提供文件。我似乎找不到适合这里的方法。我需要使用数据库,因为我使用 Google 应用引
我正在尝试制作一个宏,将下面的公式添加到单元格中,然后将其拖到整个列中并在 H 列中复制相同的公式 我想在 F 和 H 列中输入公式的数据 Range("F1").formula = "=IF(ISE
问题类似于this one ,但我想使用 OperatorPrecedenceParser 解析带有函数应用程序的表达式在 FParsec . 这是我的 AST: type Expression =
我想通过使用 sequelize 和 node.js 将这个查询更改为代码取决于在哪里 select COUNT(gender) as genderCount from customers where
我正在使用GNU bash,版本5.0.3(1)-发行版(x86_64-pc-linux-gnu),我想知道为什么简单的赋值语句会出现语法错误: #/bin/bash var1=/tmp
这里,为什么我的代码在 IE 中不起作用。我的代码适用于所有浏览器。没有问题。但是当我在 IE 上运行我的项目时,它发现错误。 而且我的 jquery 类和 insertadjacentHTMl 也不
我正在尝试更改标签的innerHTML。我无权访问该表单,因此无法编辑 HTML。标签具有的唯一标识符是“for”属性。 这是输入和标签的结构:
我有一个页面,我可以在其中返回用户帖子,可以使用一些 jquery 代码对这些帖子进行即时评论,在发布新评论后,我在帖子下插入新评论以及删除 按钮。问题是 Delete 按钮在新插入的元素上不起作用,
我有一个大约有 20 列的“管道分隔”文件。我只想使用 sha1sum 散列第一列,它是一个数字,如帐号,并按原样返回其余列。 使用 awk 或 sed 执行此操作的最佳方法是什么? Accounti
我需要将以下内容插入到我的表中...我的用户表有五列 id、用户名、密码、名称、条目。 (我还没有提交任何东西到条目中,我稍后会使用 php 来做)但由于某种原因我不断收到这个错误:#1054 - U
所以我试图有一个输入字段,我可以在其中输入任何字符,但然后将输入的值小写,删除任何非字母数字字符,留下“。”而不是空格。 例如,如果我输入: 地球的 70% 是水,-!*#$^^ & 30% 土地 输
我正在尝试做一些我认为非常简单的事情,但出于某种原因我没有得到想要的结果?我是 javascript 的新手,但对 java 有经验,所以我相信我没有使用某种正确的规则。 这是一个获取输入值、检查选择
我想使用 angularjs 从 mysql 数据库加载数据。 这就是应用程序的工作原理;用户登录,他们的用户名存储在 cookie 中。该用户名显示在主页上 我想获取这个值并通过 angularjs
我正在使用 autoLayout,我想在 UITableViewCell 上放置一个 UIlabel,它应该始终位于单元格的右侧和右侧的中心。 这就是我想要实现的目标 所以在这里你可以看到我正在谈论的
我需要与 MySql 等效的 elasticsearch 查询。我的 sql 查询: SELECT DISTINCT t.product_id AS id FROM tbl_sup_price t
我正在实现代码以使用 JSON。 func setup() { if let flickrURL = NSURL(string: "https://api.flickr.com/
我尝试使用for循环声明变量,然后测试cols和rols是否相同。如果是,它将运行递归函数。但是,我在 javascript 中执行 do 时遇到问题。有人可以帮忙吗? 现在,在比较 col.1 和
我举了一个我正在处理的问题的简短示例。 HTML代码: 1 2 3 CSS 代码: .BB a:hover{ color: #000; } .BB > li:after {
我是一名优秀的程序员,十分优秀!