项目中有时会有安全测试的要求,其中就有一条请求需要加密,现在我就来记录一下项目中的请求加密,这里只对post的请求加密,有需要其他请求方式加密,稍微修改下前端的请求拦截器、响应拦截器和后端的过滤器即可。
直接开始上代码:
一、前端vue
1. 请求拦截器:
请求拦截器的作用是将请求中的内容使用sm4加密,将sm4的秘钥使用sm2加密后放到请求头中。(这里说一个我遇到的问题,我的国密算法是用的sm-crypto这个,但是其中的sm2加密sm4的秘钥时,后端怎么也无法解密,不知道是什么原因,于是重找了一个sm2库进行加密,就没有问题了,这里是用的cdn方式使用。)
// request拦截器
service.interceptors.request.use(config => {
// 将秘钥加密后放到请求头中
config.headers.RequestKey = getRequestKey(sm4Key, false) // 请求key
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = config.url + '?'
for (const propName of Object.keys(config.params)) {
const value = config.params[propName]
const part = encodeURIComponent(propName) + '='
if (value !== null && typeof (value) !== 'undefined') {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
const params = propName + '[' + key + ']'
const subPart = encodeURIComponent(params) + '='
url += subPart + encodeURIComponent(value[key]) + '&'
}
} else {
url += part + encodeURIComponent(value) + '&'
}
}
}
url = url.slice(0, -1)
config.params = {}
config.url = url
} else if (config.method === 'post') {
if (isAddSm && config.url.indexOf('upload') === -1) {
config.data = encryptRequestData(config.data, sm4Key, config.method)
const obj = {}
obj.url = config.url
obj.sm4keyNum = sm4Key
obj.cis_req_params = config.data.cis_req_params
sm4KeyArr.push(obj)
}
}
return config
}, error => {
console.log(error)
Promise.reject(error)
})
js
2. 响应拦截器:
响应拦截器主要是对返回值进行解密
// 响应拦截器
service.interceptors.response.use(res => {
// 未设置状态码则默认成功状态
const code = res.data.code || 200
// 返回值解密
if (isAddSm) {
if (res.config.method !== 'OPTIONS') {
let i
let myKey
if (res.config.method === 'post') {
for (const item in sm4KeyArr) {
if (sm4KeyArr[item].cis_req_params === JSON.parse(res.config.data).cis_req_params) {
myKey = sm4KeyArr[item].sm4keyNum
i = item
}
}
if (code === 200) {
res.data = getResponseJson(res.data, myKey)
}
sm4KeyArr.splice(i, 1)
}
} else {
return
}
}
if (code !== 200) {
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject('error')
} else {
return res.data
}
},
error => {
console.log('err' + error)
return Promise.reject(error)
})
js
3.使用到的工具类sm.js
// 引入sm
const sm = require('sm-crypto')
// sm2公钥
const sm2Key = '04813d4d97ad31bd9d18d785f337f683233099d5abed09cb397152d50ac28cc0ba43711960e811d90453db5f5a9518d660858a8d0c57e359a8bf83427760ebcbba'
/**
* 生成SMKey
* @returns {string}
*/
export function sm4KeyGenerator () {
const sm4KeySeed = '123456789abcdef'
const keySize = 32
let sm4key = ''
while (sm4key.length < keySize) {
const randomIndex = Math.floor(Math.random() * 100) % 16
sm4key += sm4KeySeed.charAt(randomIndex)
}
return sm4key
}
/**
* 生成SM2随机请求Key
* @param sm4key
* @param isStream
* @returns {*}
*/
export function getRequestKey (sm4key, isStream) {
let key = sm4key + '-' + Date.now()
if (isStream) {
key = '1-' + key
} else {
key = '0-' + key
}
// 1 - C1C3C2,0 - C1C2C3,默认为1
const cipherMode = 0
// 解密结果
// sm.sm2.doDecrypt(sm.sm2.doEncrypt(key, publicKey, cipherMode), privateKey, cipherMode);
// 加密结果
return sm2Encrypt(key, sm2Key, cipherMode)
}
/**
* 对请求参数加密
* */
export function encryptRequestData (data, sm4key, type) {
let params
let obj
if (!data) {
if (type === 'get') {
params = 'tp=' + Date.now()
} else {
data = {
tp: Date.now()
}
params = JSON.stringify(data)
}
obj = {
cis_req_params: sm.sm4.encrypt(params, sm4key),
cis_fingerprint: sm.sm3(params)
}
} else {
if (type === 'get') {
params = Object.keys(data).map(function (key) {
return key + '=' + data[key]
}).join('&')
} else {
params = JSON.stringify(data)
}
obj = {
cis_req_params: sm.sm4.encrypt(params, sm4key),
cis_fingerprint: sm.sm3(params)
}
}
return obj
}
/**
* 返回数据解密
* @param data
* @param sm4key
* @returns {any}
*/
export function getResponseJson (data, sm4key) {
const result = sm.sm4.decrypt(data, sm4key)
return JSON.parse(result)
}
js
二、后端SpringBoot
过滤器:
/**
* @author by PHY
* @classname encryptionFilter
* @date 2021-12-15 16:27
* @description: 描述
*/
@Component
public class EncryptionFilter extends OncePerRequestFilter{
/** get请求关键字 */
private static final String GET = "GET";
/** 是否开启解密 */
@Value("${encrypt.enabled}")
private Boolean encryptEnabled;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
// 解密
if (encryptEnabled && !GET.equals(request.getMethod())) {
String requestKey = request.getHeader("RequestKey");
String decrypt = SM2Utils.decrypt(requestKey);
String sm4Key = decrypt.split("-")[1];
String requestBody = getRequestBody(request);
String requestBodyMw = null;
Map<String, String> requestBodyMap = (Map<String, String>) JSON.parse(requestBody);
// 解密请求报文
if (!"".equals(requestBody)) {
try {
requestBodyMw = SM4Utils.decryptEcb(sm4Key, requestBodyMap.get("cis_req_params"));
} catch (Exception e) {
e.printStackTrace();
}
}
request = new WrappedRequest(request, requestBodyMw);
// 加密返回值
WrappedResponse wrapResponse = new WrappedResponse(response);
chain.doFilter(request, wrapResponse);
String content = wrapResponse.getContent();
String responseBodyMw = null;
try {
// 加密
responseBodyMw = SM4Utils.encryptEcb(sm4Key, content);
} catch (Exception e) {
e.printStackTrace();
}
response.setContentLength(-1);
PrintWriter out = response.getWriter();
out.write(responseBodyMw);
out.flush();
out.close();
}
chain.doFilter(request, response);
}
/**
* 获取body体内容
* @param req 请求
* @return body体内容
*/
private String getRequestBody(HttpServletRequest req) {
try {
BufferedReader reader = req.getReader();
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
}
java
具体的代码
下载地址 有需要的朋友可以直接下载参考,记得留言。
推荐阅读
1、记录一下vue打包成app(Hbuilder X) 2、Idea 2021.2.3破解过程 3、前端面试精选-基础篇 4、Vue项目代码规范 5、.gitignore 文件常用配置