前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot + Vue 请求加密(采用国密算法)

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

作者头像
裴大头
发布2022-01-17 13:56:04
3.8K1
发布2022-01-17 13:56:04
举报
文章被收录于专栏:裴大头的专栏

项目中有时会有安全测试的要求,其中就有一条请求需要加密,现在我就来记录一下项目中的请求加密,这里只对post的请求加密,有需要其他请求方式加密,稍微修改下前端的请求拦截器、响应拦截器和后端的过滤器即可。

直接开始上代码:

一、前端vue

1. 请求拦截器:

请求拦截器的作用是将请求中的内容使用sm4加密,将sm4的秘钥使用sm2加密后放到请求头中。(这里说一个我遇到的问题,我的国密算法是用的sm-crypto这个,但是其中的sm2加密sm4的秘钥时,后端怎么也无法解密,不知道是什么原因,于是重找了一个sm2库进行加密,就没有问题了,这里是用的cdn方式使用。)

代码语言:javascript
复制
// 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)
})
复制
2. 响应拦截器:

响应拦截器主要是对返回值进行解密

代码语言:javascript
复制
// 响应拦截器
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)
})
复制
3.使用到的工具类sm.js
代码语言:javascript
复制
// 引入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)
}
复制

二、后端SpringBoot

过滤器:

代码语言:javascript
复制
/**
 * @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 "";
    }
}
复制

具体的代码

下载地址 我已经把代码上传到码云上了,有需要的朋友可以直接下载参考,记得留言。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 直接开始上代码:
    • 一、前端vue
      • 1. 请求拦截器:
      • 2. 响应拦截器:
      • 3.使用到的工具类sm.js
    • 二、后端SpringBoot
      • 具体的代码
      相关产品与服务
      手游安全测试
      手游安全测试(Security Radar,SR)为企业提供私密的安全测试服务,通过主动挖掘游戏业务安全漏洞(如钻石盗刷、服务器宕机、无敌秒杀等40多种漏洞),提前暴露游戏潜在安全风险,提供解决方案及时修复,最大程度降低事后外挂危害与外挂打击成本。该服务为腾讯游戏开放的手游安全漏洞挖掘技术,杜绝游戏外挂损失。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档