保姆级教程—前端数据加密(Vue + Axios + AES对称 + RSA非对称)

37 篇文章 0 订阅
9 篇文章 0 订阅

1. 项目背景(需求)

为了保证数据传输的安全性,利用AES+RSA混合加密,配合后端实现数据交互加密

项目环境:vue + axios

2. 实现过程(代码)

AES对称加密我们采用 CryptoJSAES加密支持AES-128、AES-192和AES-256 (AES传送门)

RSA非对称加密我们采用JSEncrypt,(RSA传送门)

第一步:npm安装两个库

npm i crypto-js jsencrypt

第二步:新建encrypt.js,封装需要用的方法

import CryptoJS from 'crypto-js'
import { JSEncrypt } from 'jsencrypt'


/**
 * 递归自然排序: key + value
 * sortObjFunc: 排序方法
 * isArraysFunc: 判断是否array
 * isObjectFunc: 判断是否object
 * isHasValues: 判断空值,null,undefined
 * 排序前: {"aaa":"111","bbb":"222","list_1":[],"list":["3","13"],"map":{"b":"2","c":"3"}}
 * 排序后: aaa111bbb222list423.852313list_1mapb2c3
 */
export const signUtil = {
  sortObjFunc: function (plaintext) {
    let signStr = ''
    let keyList = []
    for (const key in plaintext) { keyList.push(key) }
    if (keyList.length) { keyList.sort() }


    const len = keyList.length
    
    for (let i= 0; i < len; i++) {
      let value = plaintext[keyList[i]]
      if (value && signUtil.isHasValues(value)) {
        if (signUtil.isArraysFunc(value) || signUtil.isObjectFunc(value)) {
          value = signUtil.sortObjFunc(value)
        }
      }


      // 数组取value,否则取key+value(排除null,空,undefined)
      if (signUtil.isArraysFunc(plaintext)) {
        signStr += value
      } else {
        value !== null ? signStr += keyList[i] + value : signStr += keyList[i]
      }
    }

    return signStr
  },


  isArraysFunc (item) { return Object.prototype.toString.call(item) === '[object Array]' },


  isObjectFunc (item) { return Object.prototype.toString.call(item) === '[object Object]' },


  isHasValues (item) { return item !== 'null' || item !== 'undefined' || item !== 0 }
}




/**
 * aes加密
 * genKey: 获取key
 * encrypt: AES加密
 * decrypt: AES解密
 */
export const aesUtil = {
  genKey: function(expect = 16) {
    const random = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
    let str = ''
    while (str.length < expect) { str += random.charAt(Math.random() * random.length) }
    return str
  },


  encrypt: function(plaintext, key) {
    if (plaintext instanceof Object) { plaintext = JSON.stringify(plaintext) }
    let encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(plaintext), CryptoJS.enc.Utf8.parse(key), {
      mode: CryptoJS.mode.ECB,
      padding: CryptoJS.pad.Pkcs7
    })
    return encrypted.toString()
  },


  decrypt: function(ciphertext, key) {
    let decrypt = CryptoJS.AES.decrypt(ciphertext, CryptoJS.enc.Utf8.parse(key), {
      mode: CryptoJS.mode.ECB,
      padding: CryptoJS.pad.Pkcs7
    })
    let decString = CryptoJS.enc.Utf8.stringify(decrypt).toString()
    if (decString.charAt(0) === '{' || decString.charAt(0) === '[') {
      decString = JSON.parse(decString)
    }

    return decString
  }
}




/**
 * rsa加密
 * encrypt: 公钥加密
 * decrypt: 私钥解密
 * ensign: rsa签名
 * design: rsa验签
 */
const encryptor = new JSEncrypt({ default_key_size: 1024 })
export const rsaUtil = {
  encrypt: function (key, publicKey) {
    publicKey && encryptor.setPublicKey(publicKey)
    return encryptor.encrypt(key)
  },


  decrypt: function (key, privateKey) {
    privateKey && encryptor.setPrivateKey(privateKey)
    return encryptor.decrypt(key)
  },


  ensign: function (data, privateKey){
    privateKey && encryptor.setPrivateKey(privateKey)
    return encryptor.sign(data, CryptoJS.SHA256, 'sha256')
  },


  design: function (data, signature, publicKey){
    if (signature && publicKey) {
      encryptor.setPrivateKey(publicKey)
      return encryptor.verify(data, signature, CryptoJS.SHA256)
    }
  }
}




/**
 * RSA秘钥对
 * publicKey: 前端rsa公钥
 * privateKey: 前端rsa私钥
 * servePublicKey: 服务端rsa公钥
 */
export const publicKey = encryptor.getPublicKey()
export const privateKey = encryptor.getPrivateKey()
export const servePublicKey = '服务端公钥'

第三步:改造Axios

import axios from 'axios'
import { signUtil, aesUtil, rsaUtil, servePublicKey, privateKey } from 'encryptn'



axios.defaults.timeout = 20000;
axios.defaults.baseURL = 'http://8.8.8.8:8080'
axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8';
axios.defaults.headers.post['Access-Control-Allow-Origin'] = '*';




// 请求拦截器
axios.interceptors.request.use((config) => {
  // other code here...  

  if (config.data) {
    // 生成签名
    const signKey = signUtil(config.data)
    const newSignKey = rsaUtil.ensign(signKey, privateKey)


    // AES随机秘钥
    const romkey = aesUtil.genKey()


    // 服务端公钥对随机秘加密
    const aesKey = rsaUtil.encrypt(romkey, servePublicKey)


    // AES + 随机秘对data体加密
    config.data['sign'] = newSignKey
    const aesData = aesUtil.encrypt(config.data, romkey)


    const newData = { data: aesData, key: aesKey }
    config.data = newData
  }


  return config;
}, (error) => {
  return Promise.reject(error);
});




// 响应拦截器
axios.interceptors.response.use((res) => {
  // other code here

  const { data, key } = res.data
  
  
  // 客户端私钥解密key
  const rsaKey = rsaUtil.decrypt(key, privateKey)

  
  // AES + key解密data
  const newData = aesUtil.decrypt(data, rsaKey)

  
  // 重新生成签名验证
  if (newData.sign) {
    const copyData = JSON.parse(JSON.stringify(newData))
    delete copyData.sign
    const data = signUtil(copyData)
    const flag = rsaUtil.design(data, newData.sign, servePublicKey)
    if (!flag) { return Promise.reject({ message: '签名失败' }) }
    return Promise.resolve(res.data)
  } else {
    return Promise.reject({ message: '请求异常' })
  }

}, (error) => {
    return Promise.reject(error);
});

## 3. 注意问题

  • 签名排序统一,需要和后端签名一致才可以通过
  • 加密长度统一,这里统一使用1024,具体前后端协商
  • 数据结构统一,返回体的数据结构一致,方便后面验证签名
  • 前端加密一定程度增加了网络攻击的难度,最好还是上https

Vue是一个用于构建用户界面的渐进式框架,而axios是一个基于Promise的HTTP库,用于发送异步请求。在Vue中使用axios可以实现与后端服务器进行数据交互。 首先,在Vue项目中引入axios。可以通过npm安装axios,并在项目中引入: ```javascript import axios from 'axios' ``` 然后,在Vue组件中使用axios发送请求。可以使用axios的get、post等方法发送HTTP请求,并通过then和catch处理返回的数据: ```javascript axios.get('/api/data') // 发送GET请求 .then(response => { // 处理返回的数据 this.data = response.data }) .catch(error => { // 处理请求错误 console.log(error) }) ``` 接下来,使用返回的数据更新Vue组件的数据。可以将返回的数据保存在Vue实例的data属性中,然后在模板中使用该数据: ```javascript export default { data() { return { data: null } } } ``` ```html <template> <div> <p>{{data}}</p> </div> </template> ``` 最后,使用echarts将数据可视化。可以在Vue生命周期的钩子函数中初始化echarts实例,并通过setData方法更新数据: ```javascript import echarts from 'echarts' export default { mounted() { // 初始化echarts实例 const myChart = echarts.init(document.getElementById('chart')) // 设置数据 myChart.setOption({ series: [{ data: this.data }] }) } } ``` 以上是使用Vue+axios+echarts进行数据交互和可视化的基本步骤。使用axios发送请求获取数据,然后使用echarts将数据进行可视化展示。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值