基于sm-crypto的sm4的请求加密&响应加密

发布时间 2023-10-24 14:12:32作者: 致爱丽丝

有时候需要对项目的请求和返回值进行加密请求,因而笔者使用了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
  // 如果要校验请求类型,就需要先在这里声明`contenttype`
  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
})

以上。