基于sm-crypto的sm4的请求/响应与加/解密
有时候需要对项目的请求和返回值进行加密请求,因而笔者使用了sm4
,读者也可以使用别的库如md5
封装加解密:
// ciphertext.js
const sm4 = require('sm-crypto').sm4
// 此为密文key,非常重要
export const CIPHERTEXT = `wzdxcskwzdxcskwzdxcskwzdxcskwzdxcsk` //我真的想吃烧烤我真的想吃烧烤我真的想吃烧烤我真的想吃烧烤
/**
* 参考:
* https://github.com/JuneAndGreen/sm-crypto#sm4
*
* */
// 数据加密 用于axios请求拦截器
export const encryptSm4 = ( requestParams = '' )=>{
if (!requestParams) return
return sm4.encrypt(requestParams, CIPHERTEXT)
}
// 数据解密 用于axios相应拦截器
export const decryptSm4 = ( result = '' )=>{
if (!result) return
return sm4.decrypt(result, CIPHERTEXT)
}
响应/请求拦截中使用加解密方法,以下只写要改动的,其余的可以不变
// request.js
import axios from "axios";
import store from "../store";
import { encryptSm4, decryptSm4 } from "./ciphertext";
//生成axios实例
const service = axios.create({
// axios默认统一数据
timeout: 120000, //指定请求超时的毫秒数(0 表示无超时时间)
// baseURL:xxxx, //可能有不同类型的api地址,可以在这里设置baseurl,后续中用以对比是否源于指定的url
// 如果要校验请求类型,就需要先在这里声明`content-type`
headers: {
"Content-Type": "application/json" // 设置默认json格式以规避在拦截器中拿不到 content-type问题
}
});
// 确认config配置项
const configCheck = (conf, type) => {
// 此处可根据需要自定义校验
const isPost = conf.method.toUpperCase() === `POST`; // 是否post
// const jsonReg = /application\/json/; // 正则表达式匹配'application/json'
// const isJSON = jsonReg.test(conf.headers[`Content-Type`]) // 是否JSON
const isFromBaseURL = conf.baseURL.includes(process.env.VUE_APP_BASE_API); //是否基于baseurl
// console.log('isFromBaseURL :>> ', conf.baseURL, isFromBaseURL);
// console.log('process.env.VUE_APP_BASE_API :>> ', process.env.VUE_APP_BASE_API);
return {
send: [isFromBaseURL, isPost],
receive: [isFromBaseURL]
}[type].every(v => v);
};
// 请求拦截配置
/**
* 有以下方可进行加密:
* configCheck 为true
* 明确`store`中`encryption`为1 后端控制是否需要将请求/响应加密
*/
service.interceptors.request.use(config=>{
...other code...
const isEncrypted = store.encryption === 1
if (isEncrypted && configCheck(config, `send`)) {
try {
const reqData = encryptSm4(JSON.stringify(config[`data`]));
config[`data`] = reqData ? reqData : config[`data`];
} catch (error) {
console.log("error :>> ", error);
}
}
...other code...
return config
})
// 响应拦截
service.interceptors.response.use(response=>{
let res = response.data; // 这里故意用了`let` 因为加密态下可能要改
// ---------------------- 此处为解密校验
const isEncrypted = store.encryption === 1
// 加密态下返回的只有一堆加密str,因而没有`res.cdoe`属性
if (!res.code && configCheck(response.config, `receive`) && isEncrypted) {
try {
// 解析加密数据
res = JSON.parse(decryptSm4(res));
if (typeof res === `string`) {
console.log(`--------------------`);
console.log("res :解析失败返回值>> ", res);
console.log("失败接口路径为 :>> ", response.config.url);
console.log("接口方法为 :>> ", response.config.method);
console.log(`--------------------`);
}
// 解密后数据不一定是正常返回值,因而这里做了简单校验
if (res.code && Number(res.code) !== 200) {
Message({
message: res.msg || "当前网络繁忙,请稍后重试!",
type: "error",
duration: 3 * 1000
});
return Promise.reject(
new Error(res.msg || "当前网络繁忙,请稍后重试")
);
}
} catch (error) {
console.log(`--------------------`);
console.log("error :>解析异常告警> ", error);
console.log("res :解析失败返回值>> ", res);
console.log("失败接口路径为 :>> ", response.config.url);
console.log("接口方法为 :>> ", response.config.method);
console.log(`--------------------`);
}
}
// 👆----------------------------------- 此处为解密校验
// 接下来可以写正常明文校验,不表
return res
})
几点要求:
-
ciphertext.js
的CIPHERTEXT
是每次前后端交互时加解密的依据,必须要前后端统一 -
一个标准加密态下的请求/返回,在控制台中的入参&出参回显应该是一堆乱码,可以以此来判断是否使用了密文
加密态下的入参:
加密态下的出参:
以上。