ToB企服应用市场:ToB评测及商务社交产业平台

标题: 前端使用 crypto-js 库 aes加解密 [打印本页]

作者: 忿忿的泥巴坨    时间: 2024-12-6 03:46
标题: 前端使用 crypto-js 库 aes加解密
前端使用crypto-js AES 加密解密

CryptoJS是一个JavaScript加密算法库,用于在客户端欣赏器中执行加密和解密操纵。它提供了一系列常见的加密算法,如AES、DES、Triple DES、Rabbit、RC4、MD5、SHA-1等等。
AES

工作原理

AES(高级加密标准)是一种对称加密算法,即加密和解密使用相同的密钥。它可以加密长度为128、192和256位的数据块,并使用128位的密钥进行加密。AES算法使用了固定的块长度和密钥长度,并且被广泛应用于许多安全协媾和标准中,例如SSL/TLS、SSH、IPSec等。
在AES加密中,明文被分成128位的块,每个块使用相同的密钥进行加密。加密过程包括以下步调:

   解密过程与加密过程雷同,只是将加密过程中的步调反过来。必要注意的是,解密的过程中使用的是相同的密钥和轮密钥。由于AES是一种块加密算法,因此在加密过程中,必要对数据进行填充,确保数据块大小为128位。
  AES算法的长处


   github地址: https://github.com/brix/crypto-js
    cryptojs文档: https://cryptojs.gitbook.io/docs/#encoders
    在线aes加密解密工具: http://tool.chacuo.net/cryptaes
  使用JSEncrypt进行RSA加密的基本步调如下:

前端:

后端:

   必要注意的是,由于JSEncrypt是在客户端上执行的,以是密钥在传输过程中可能会存在安全风险。为了确保数据的安全性,建议在客户端和服务器之间使用安全的通信协议进行数据传输,并在服务器端进行进一步的安全验证和处理。
  安装

script 标签嵌入
  1. <script src="https://cdn.bootcdn.net/ajax/libs/crypto-js/4.1.1/aes.min.js"></script>
复制代码
npm 或 yarn 安装
  1. npm install crypto-js
  2. yarn add crypto-js
复制代码
CommonJS
  1. const CryptoJS = require('crypto-js');
复制代码
ES module:
  1. import CryptoJS from 'crypto-js';
复制代码
封装加密和解密

  1. // 创建key.js文件
  2. import JSEncrypt from 'jsencrypt'
  3. const encrypt = new JSEncrypt();
  4. let publicKey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCeCDcnFrS7DIRbvZLHreVUzaMbAFy2DYmioxBK606urY4rVR8IgLgUhnyw2/GQ99pyr8lGtqPeOoapantw1XwEVyi74MDxs4UDL8j4OZR1Es7HVGOB0GwKWobdU9cm/1iDwGyouSmijxKyAePg6KsLNgbjDPYZRS11bYEuZ8/RLQIDAQAB';
  5. // 设置公钥
  6. encrypt.setPublicKey('-----BEGIN PUBLIC KEY-----' + publicKey + '-----END PUBLIC KEY-----')
  7. const random = (length) => {
  8.   var str = Math.random().toString(36).substr(2);
  9.   if (str.length >= length) {
  10.     return str.substr(0, length);
  11.   }
  12.   str += random(length - str.length);
  13.   return str;
  14. }
  15. export const rsaEncode = (src) => {
  16.   // 加密数据
  17.   let data = encrypt.encrypt(src);
  18.   return data
  19. };
  20. export const getKey = () => {
  21. // 生成私钥和偏移量
  22.   let key = { key: random(16), iv: random(16) };
  23.   // 对私钥和偏移量加密
  24.   let code = rsaEncode(key.key + ',' + key.iv);
  25.   window.codeArr = window.codeArr || {};
  26.   // 存入客户端
  27.   codeArr[code] = key;
  28.   return {
  29.     key, code
  30.   }
  31. };
  32. export const getAesKey = (aes) => {
  33.   console.log('aes: ', aes);
  34.   let key = JSON.parse(JSON.stringify(codeArr[aes]));
  35.   // 从客户端获取到 key
  36.   delete codeArr[aes];
  37.   return key
  38. };
  39. window.getKey = getKey
  40. window.rsaEncode = rsaEncode
复制代码
  1. // 创建 encrypt.js 文件import CryptoJS from 'crypto-js';
  2. // ------------AES 加密-------------function getAesString(data, key, iv) {//加密  let keys = CryptoJS.enc.Utf8.parse(key)  let vis = CryptoJS.enc.Utf8.parse(iv)  let encrypt = CryptoJS.AES.encrypt(data, keys, {    iv: vis, //iv偏移量 CBC需加偏移量    mode: CryptoJS.mode.CBC, //CBC模式    // mode: CryptoJS.mode.ECB, //ECB模式    padding: CryptoJS.pad.Pkcs7 //padding处理  });  // debugger  return encrypt.toString(); //加密完成后,转换成字符串}// ------------AES 解密-------------function getDAesString(encrypted, key, iv) { // 解密  var key  = CryptoJS.enc.Utf8.parse(key);  var iv   = CryptoJS.enc.Utf8.parse(iv);  var decrypted =CryptoJS.AES.decrypt(encrypted,key,{    iv:iv,    mode:CryptoJS.mode.CBC,    padding:CryptoJS.pad.Pkcs7  });  return decrypted.toString(CryptoJS.enc.Utf8);}// AES 对称秘钥加密const aes = {  en: (data, key) => getAesString(data, key.key, key.iv),  de: (data, key) => getDAesString(data, key.key, key.iv)};export { aes };
复制代码
  publicKey 可以通过发哀求后端返回,也可以自己界说,要求前后端划一
  封装 axios 拦截器进行加密解密

  1. /**
  2. *
  3. * http配置
  4. *
  5. */
  6. // 引入axios以及element ui中的loading和message组件
  7. import { aes } from "@/util/encrypt.js";
  8. import { getKey, getAesKey } from "@/config/key.js";
  9. import axios from "axios";
  10. import store from "../store";
  11. import router from "../router/router";
  12. import { Loading, Message } from "element-ui";
  13. import { getSessStore, setSessStore } from "@/util/store";
  14. // 超时时间
  15. if (store.online) axios.defaults.timeout = 20000;
  16. else axios.defaults.timeout = 0;
  17. //跨域请求,允许保存cookie
  18. axios.defaults.withCredentials = true;
  19. // 统一加解密
  20. const Unify = {
  21.   // 统一加密方法
  22.   en(data, key) {
  23.     // 1.aes加密
  24.     let aesStr = aes.en(JSON.stringify(data), key);
  25.     return aesStr;
  26.   },
  27.   // 统一解密
  28.   de(aesStr, key) {
  29.     // 1.aes解密
  30.     let dataStr = aes.de(aesStr, key);
  31.     // 3.转json对象
  32.     let data = JSON.parse(dataStr);
  33.     return data;
  34.   },
  35. };
  36. let loadinginstace;
  37. let cfg, msg;
  38. msg = "服务器君开小差了,请稍后再试";
  39. function ens(data) {
  40.   // debugger
  41.   let src = [...data];
  42.   src = JSON.stringify(src);
  43.   let dataJm = aes.en(src);
  44.   return dataJm;
  45. }
  46. function des(data) {
  47.   // debugger
  48.   let src = [...data];
  49.   let dataJm = aes.de(src);
  50.   dataJm = JSON.parse(dataJm);
  51.   return dataJm;
  52. }
  53. const cancelToken = axios.CancelToken
  54. const source = cancelToken.source()
  55. //HTTPrequest拦截
  56. axios.interceptors.request.use(
  57.   function (config) {
  58.     console.log(config.data, "加密前入参---");
  59.     config.cancelToken = source.token; // 全局添加cancelToken
  60.     loadinginstace = Loading.service({
  61.       fullscreen: true,
  62.     });
  63.     if (store.getters.token) {
  64.       let info = getSessStore("token");
  65.       // console.log("info", info);
  66.       config.headers["Authorization"] = "Bearer " + info; // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
  67.     }
  68.     const contenttype = config.headers.common["Accept"];
  69.     let types = contenttype.includes("application/json");
  70.     let key = getKey(); // 获取密钥
  71.     config.headers["aes"] = key.code; // 将 aes 的 code 设置到请求头,传给后端解密
  72.     config.headers["name"] = "send";
  73.     if (types) {
  74.       if (config.method == "post" || config.method == "put") {
  75.         if (config.data) {
  76.           config.headers["crypto"] = true;
  77.           config.headers["content-type"] = "application/json";
  78.           let data = {
  79.             body: config.data,
  80.           };
  81.           let dataJm = Unify.en(data, key.key); // 加密 post 请求参数
  82.           config.data = dataJm;
  83.         }
  84.       }
  85.     }
  86.     return config;
  87.   },
  88.   (error) => {
  89.     return Promise.reject(error);
  90.   }
  91. );
  92. //HTTPresponse拦截
  93. axios.interceptors.response.use(
  94.   (response) => {
  95.     loadinginstace.close();
  96.     let res = response.data || {};
  97.     if (response.headers["date"]) {
  98.       store.commit("setServiceTime", response.headers.date);
  99.     }
  100.     if (res.crypto) {
  101.       try {
  102.         let key = getAesKey(response.headers.aes); // 拿到加密后的key
  103.         if (!key) {
  104.           message("获取密钥异常", "error");
  105.           return Promise.reject(res);
  106.         }
  107.         // debugger
  108.         res = Unify.de(res.body, key); // 解密数据
  109.         response.data = res;
  110.       } catch (err) {
  111.         message("系统异常:" + err.message, "error");
  112.         return Promise.reject(err);
  113.       }
  114.     }
  115.     // debugger
  116.     if (res.code === 1) {
  117.       message(res.msg, "error");
  118.       return Promise.reject(res);
  119.     }
  120.     console.log(response, "解密后response");
  121.     return response;
  122.   },
  123.   (error) => {
  124.     console.log("错误信息", error);
  125.     loadinginstace.close();
  126.     const res = error.response || {};
  127.     if (res.status === 478 || res.status === 403 || res.status === 401) {
  128.       let resMsg = res.data.msg ? res.data.msg : res.data.data
  129.       if (res.status === 403) {
  130.         message('服务授权失败,请联系管理添加权限!', "error");
  131.       } else {
  132.         message(resMsg, "error");
  133.       }
  134.       let flg = res.data.msg.includes('当前登录状态已失效')
  135.       if (res.status === 478 && flg) {
  136.         //token失效
  137.         source.cancel('登录信息已过期'); // 取消其他正在进行的请求
  138.         store.dispatch("FedLogOut").then(() => {
  139.           router.push("/login"); ///test
  140.         });
  141.       }
  142.     } else if (res.status === 400) {
  143.       message(res.data.error_description, "error");
  144.     } else if (res.status === 202) {
  145.       //三方未绑定
  146.       this.$router.push({
  147.         path: "/",
  148.       });
  149.     } else if (res.status === 503 || res.status === 504) {
  150.       //服务异常
  151.       message(res.data, "error");
  152.     } else if (
  153.       (res.status === 401 && res.statusText == "Unauthorized") ||
  154.       res.data.error == "invalid_token" ||
  155.       res.data.error == "unauthorized"
  156.     ) {
  157.       //token失效
  158.       store.dispatch("FedLogOut").then(() => {
  159.         router.push("/login"); ///test
  160.       });
  161.     } else {
  162.       message(res.data.message, "error");
  163.     }
  164.     return Promise.reject(error);
  165.   }
  166. );
  167. export function message(text, type) {
  168.   let t = text ? text : "服务或网络异常!"
  169.   Message({
  170.     message: t,
  171.     type: type,
  172.     duration: 30 * 1000,
  173.     center: true,
  174.     showClose: true
  175.   });
  176. }
  177. export default axios;
复制代码
总结

加密: 使用 JSEncrypt 天生私钥 key 和偏移量 iv 并将密钥 key 和 iv 加密得到 code, 使用 CryptoJS.AES.encrypt() 和 key 加密哀求数据,将 加密后的 code 设置在哀求头,后端获取加密后 code 进行解密得到私钥 key ,再对哀求数据解密得到原始数据
解密: 前端获取响应头的key,从window得到私钥 key, 使用 CryptoJS.AES.decrypt() 方法对响应数据进行解密,得到原始数据

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4