裴大头-秦可爱

裴大头-秦可爱

SpringBoot + Vue 请求加密(采用国密算法)

发表于 2021-12-16
裴大头
阅读量 462
更新于 2024-06-28

项目中有时会有安全测试的要求,其中就有一条请求需要加密,现在我就来记录一下项目中的请求加密,这里只对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 文件常用配置

关注公众号 width:400px;height:150px;

评论
来发一针见血的评论吧!
表情

快来发表评论吧~

推荐文章
  • JavaScript 的事件循环机制

    1点赞1评论

  • Element UI 级联选择器 el-cascader 实现懒加载和搜索功能

    1点赞0评论

  • Java 23种设计模式——适配器模式(Adapter)

    1点赞0评论

  • Vue项目代码规范

    1点赞1评论

  • Java 23种设计模式——单例模式(Singleton)

    0点赞1评论

Crafted with by Pei你看雪

小破站居然运行了 1048 天访客 26777

© 2023 Pei你看雪鲁ICP备19037910号-2