【方案】前后端数据传输加密实现方案 - 后端实现

[方案系列文章说明]: 该类型的文章是我在使用和学习中认为不错的解决办法,当然会有更好的方案,也一定会有, 所以文章具有一定的局限性, 请结合自己的思考辩证的看.

继续上代码

1, 加密工具类

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
/**
* 前后端请求加解密工具
*
* @Description
* @Author xingzhi.lv
* @Version 1.0
* @Date 2021/7/2 15:36
*/
public class RequestAESUtils {
private static final String ENCRYPT_TYPE = "AES";
private static final String CIPHER_MODEL = "AES/ECB/PKCS5Padding";

/**
* 初始化加密
*
* @return
* @throws Exception
*/
private static Cipher createAesCipher() throws Exception {
return Cipher.getInstance(CIPHER_MODEL);
}

/**
* AES方式加密
* Base64输出
*
* @param content
* @return
* @throws Exception
*/
public static String encryptByAES(String content, String key) throws Exception {
Cipher cipher = createAesCipher();
KeyGenerator keyGenerator = KeyGenerator.getInstance(ENCRYPT_TYPE);
keyGenerator.init(128, new SecureRandom(key.getBytes()));
SecretKeySpec secretKeySpec = new SecretKeySpec(Arrays.copyOf(key.getBytes(StandardCharsets.UTF_8), 16), ENCRYPT_TYPE);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] bytes = cipher.doFinal(content.getBytes());
// 对字节数组加密生成字符串
BASE64Encoder base64encoder = new BASE64Encoder();
String result = base64encoder.encode(bytes);
return result;
}

/**
* AES方式解密
* Base64输入
*
* @param content
* @return
* @throws Exception
*/
public static String decryptByAes(String content, String key) throws Exception {
// 对字符串解密形成数组-> 再转为对应编码字符串
BASE64Decoder dencode = new BASE64Decoder();//创建解码对象
byte[] bytes = dencode.decodeBuffer(content);//得到字节数组
Cipher cipher = createAesCipher();
KeyGenerator keyGenerator = KeyGenerator.getInstance(ENCRYPT_TYPE);
keyGenerator.init(128, new SecureRandom(key.getBytes()));
SecretKeySpec secretKeySpec = new SecretKeySpec(Arrays.copyOf(key.getBytes(StandardCharsets.UTF_8), 16), ENCRYPT_TYPE);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] resultDecryptByte = cipher.doFinal(bytes);
return new String(resultDecryptByte);
}

// public static void main(String[] args) throws Exception {
// String encryContent = RequestAESUtils.encryptByAES("138", "0000000000000000");
// System.out.println(encryContent);
// String content = RequestAESUtils.decryptByAes(encryContent, "0000000000000000");
// System.out.println(content);
// System.out.println(RequestAESUtils.decryptByAes("1BUqgKqSChvYM6xwB5wIzA==", "0000000000000000"));
// }

}

2, 过滤器

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
133
134
135
136
137
138
139
/**
* web请求加解密
* <p>
* 要求header:
* [
* contentType: contentType支持application/json,application/x-www-form-urlencoded
* isEncrypt: true/false 解密/不解密
* decryptFields: user|age|id 需要解密的字段,以|分割
* method: POST/GET 支持POST/GET
* ]
*
* @Description
* @Author xingzhi.lv
* @Version 1.0
* @Date 2021/7/2 11:38
*/
// TODO extends oncefilter
public class WebDecryptFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(WebDecryptFilter.class);
private final ObjectMapper objectMapper = new ObjectMapper();
private final static String POST = "POST";
private final static String GET = "GET";
private final static String HEADER_ENCRYPT_STATUS = "isEncrypt";
private final static String HEADER_DECRYPT_FIELDS = "encryptFields";
private final static String HEADER_DECRYPT_FIELDS_SPLIT = "|";
// 秘钥
public static final String REQUEST_SECRET = "0000000000000000";

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
// TODO 测试文件上传有没有影响,各种请求类型有没有影响
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
throw new ServletException("OncePerRequestFilter just supports HTTP requests");
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 不管什么类型都转换
WebDecryptFilterRequest webDecryptFilterRequest = new WebDecryptFilterRequest(httpRequest, httpRequest.getParameterMap());
try {
if (!checkDecryptCondition(webDecryptFilterRequest)) {
filterChain.doFilter(webDecryptFilterRequest, response);
return;
}
Map<String, Object> parameterMap;
String contentType = webDecryptFilterRequest.getContentType();
if (contentType.contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
parameterMap = dealUrlencodeVlue(webDecryptFilterRequest);
} else if (contentType.contains(MediaType.APPLICATION_JSON_VALUE)) {
parameterMap = dealJsonVlue(webDecryptFilterRequest);
} else {
filterChain.doFilter(webDecryptFilterRequest, response);
return;
}
webDecryptFilterRequest.setParameterMap(parameterMap);
logger.info("前后端数据AES解密, 解密前内容:[{}]", parameterMap);
parameterMap = decryptMapValues(parameterMap);
logger.info("前后端数据AES解密, 解密后内容:[{}]", parameterMap);
// 解密数据绑定到自定义request
webDecryptFilterRequest.setParameterMap(parameterMap);
filterChain.doFilter(webDecryptFilterRequest, response);
} catch (Exception e) {
logger.error("前后端数据AES解密, 解密异常!", e);
filterChain.doFilter(webDecryptFilterRequest, response);
return;
}
}

/**
* 获取参数内容-urlencode
*
* @param request
*/
private Map<String, Object> dealUrlencodeVlue(HttpServletRequest request) {
Map<String, Object> parameterMap = new LinkedHashMap<>();
for (String reqParamMapKey : request.getParameterMap().keySet()) {
parameterMap.put(reqParamMapKey, request.getParameterMap().get(reqParamMapKey)[0]);
}
return parameterMap;
}

/**
* 获取参数内容-json
*
* @param webDecryptFilterRequest
*/
private Map<String, Object> dealJsonVlue(WebDecryptFilterRequest webDecryptFilterRequest) throws IOException {
Map<String, Object> parameterMap = new LinkedHashMap<>();
String data = StreamUtils.copyToString(webDecryptFilterRequest.getInputStream(), StandardCharsets.UTF_8);
parameterMap = objectMapper.readValue(data, parameterMap.getClass());
return parameterMap;
}

/**
* 解密
*
* @param parameterMap
* @return
* @throws Exception
*/
private Map<String, Object> decryptMapValues(Map<String, Object> parameterMap) throws Exception {
for (String paramKey : parameterMap.keySet()) {
parameterMap.put(paramKey, RequestAESUtils.decryptByAes(parameterMap.get(paramKey).toString(), REQUEST_SECRET));
}
return parameterMap;
}

/**
* 解密条件校验
*
* @return false:不解密 true:解密
*/
private boolean checkDecryptCondition(HttpServletRequest request) {
String isEncryStr = request.getHeader(HEADER_ENCRYPT_STATUS);
String decryptFieldsStr = request.getHeader(HEADER_DECRYPT_FIELDS);
String[] decryptFields = {};
if (StringUtils.isNotBlank(decryptFieldsStr)) {
decryptFields = decryptFieldsStr.split(HEADER_DECRYPT_FIELDS_SPLIT);
}
// 解密开关,解密字段列表
boolean isEncry = Boolean.parseBoolean(isEncryStr);
if (!isEncry || decryptFields.length == 0) {
return false;
}
// 请求类型
String method = request.getMethod();
if (!POST.equals(method) && !GET.equals(method)) {
return false;
}
return true;
}

@Override
public void init(FilterConfig filterConfig) throws ServletException {
}

@Override
public void destroy() {
}
}

3, 重写Request

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
/**
* 自定义解密Request
*
* @Description
* @Author xingzhi.lv
* @Version 1.0
* @Date 2021/7/2 14:10
*/
public class WebDecryptFilterRequest extends HttpServletRequestWrapper {

// 解密参数存储
private Map<String, String[]> parameterMap = new LinkedHashMap<>();
// 原始数据流缓存
private byte[] bodyCache;

public WebDecryptFilterRequest(HttpServletRequest request, Map<String, String[]> parameterMap) throws IOException {
super(request);
bodyCache = StreamUtils.copyToByteArray(request.getInputStream());
Map<String, String[]> copyParameterMap = new LinkedHashMap<>(parameterMap.size());
if(Objects.nonNull(parameterMap)){
for (String key : parameterMap.keySet()) {
copyParameterMap.put(key,parameterMap.get(key));
}
}
this.parameterMap = copyParameterMap;
}

@Override
public Enumeration<String> getParameterNames() {
Vector<String> vector = new Vector<>(parameterMap.keySet());
return vector.elements();
}

@Override
public String getParameter(String name) {
String[] results = parameterMap.get(name);
if (results != null && results.length > 0) {
return results[0];
} else {
return null;
}
}

@Override
public String[] getParameterValues(String name) {
return parameterMap.get(name);
}

@Override
public Map<String, String[]> getParameterMap() {
return parameterMap;
}

public void setParameterMap(Map<String, Object> parameterMap) {
parameterMap.forEach((key, value) -> {
if (value != null) {
this.parameterMap.put(key, new String[]{value.toString()});
}
});
}

@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}

@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream bais = new ByteArrayInputStream(bodyCache);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bais.read();
}

@Override
public boolean isFinished() {
return false;
}

@Override
public boolean isReady() {
return false;
}

@Override
public void setReadListener(ReadListener listener) {
}
};
}
}

4, 配置过滤器

1
2
3
4
5
6
7
8
9
<filter>
<filter-name>webDecryptFilter</filter-name>
<filter-class>com.qichengcx.common.web.fliter.WebDecryptFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>webDecryptFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>