vue-router全局参数自动加密、解密-jsencrypt

通过query传参,url上的值是明文展示,想对参数加密一下,这样就无法被手动修改一些值,提高安全性
如果在每次传参时单独做加密显得很麻烦,希望直接在router配置,不对其他组件做修改

1.安装jsencrypt
npm install jsencrypt
2.准备encryption.js和query.js

准备encryption.js文件,对加密解密进行封装

//encryption.js
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
//处理字符串过长加密报错 解决Message too long for RSA
import './jsencryptlong.js';
// 密钥对生成 http://web.chacuo.net/netrsakeypair
const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' +
  'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='

const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' +
  '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' +
  'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' +
  'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' +
  'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' +
  'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' +
  'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' +
  'UP8iWi1Qw0Y='
// 加密
export function encrypt(txt) {
  const encryptor = new JSEncrypt();
  encryptor.setPublicKey(publicKey); // 设置公钥
  // 对数据进行解密
  if (txt.length >= 117) {
    return encryptor.encryptLong2(txt);
  } else {
    return encryptor.encrypt(txt);
  }
}

// 解密
export function decrypt(txt) {
  const encryptor = new JSEncrypt();
  encryptor.setPrivateKey(privateKey); // 设置私钥
  // 对数据进行解密
  if (txt.length >= 117) {
    return encryptor.decryptLong2(txt);
  } else {
    return encryptor.decrypt(txt);
  }
}

准备query.js(与encryption.js同级),对url上参数进行加密解密

//query.js
import { encrypt, decrypt } from './encryption.js';
const encodeReserveRE = /[!'()*]/g;
const encodeReserveReplacer = (c) => '%' + c.charCodeAt(0).toString(16);
const commaRE = /%2C/g;

const encode = (str) => encodeURIComponent(str).replace(encodeReserveRE, encodeReserveReplacer).replace(commaRE, ',');

const decode = decodeURIComponent;

/**
* 判断字符串是否是base64
* @param { string } str
* @returns { boolean }
*/
function isBase64(str) {
 if (str === '' || str.trim() === '') {
   return false;
 }
 try {
   return btoa(atob(str)) == str;
 } catch (err) {
   return false;
 }
}

/**
* 序列化对象 并加密
* @param {Object} obj
*/
export const stringifyQuery = (obj) => {
 const res = obj
   ? Object.keys(obj)
       .map((key) => {
         const val = obj[key];

         if (val === undefined) {
           return '';
         }

         if (val === null) {
           return encode(key);
         }

         if (Array.isArray(val)) {
           const result = [];
           val.forEach((val2) => {
             if (val2 === undefined) {
               return;
             }
             if (val2 === null) {
               result.push(encode(key));
             } else {
               result.push(encode(key) + '=' + encode(val2));
             }
           });
           return result.join('&');
         }

         return encode(key) + '=' + encode(val);
       })
       .filter((x) => x.length > 0)
       .join('&')
   : null;

 return res ? `?${encrypt(res)}` : '';
};

/**
* 解密  反序列化字符串参数
* @param {String}} query
*/
export const parseQuery = (query) => {
 const res = {};

 query = query.trim().replace(/^(\?|#|&)/, '');

 if (!query) {
   return res;
 }

 // 解密
 query = isBase64(query) ? decrypt(query) : query;

 query.split('&').forEach((param) => {
   const parts = param.replace(/\+/g, ' ').split('=');
   const key = decode(parts.shift());
   const val = parts.length > 0 ? decode(parts.join('=')) : null;

   if (res[key] === undefined) {
     res[key] = val;
   } else if (Array.isArray(res[key])) {
     res[key].push(val);
   } else {
     res[key] = [res[key], val];
   }
 });

 return res;
};

3.对router进行配置,修改默认的stringifyQuery和parseQuery,使其使用我们query.js中封装的
import { stringifyQuery, parseQuery } from '../utils/query' //路径位置根据实际修改
const router = createRouter({
  history: createWebHashHistory(),
  stringifyQuery, // 序列化query参数
  parseQuery, // 反序列化query参数
  routes,
})
e172f119f6a84784b69c6037d073d0bf.png

组件内:正常传递与获取query,无需做额外修改

router.push({ name: 'test', query: { type:'create' } })
console.log(route.query.type) //create
4、当加密字符串过长时,控制台报错(解决Message too long for RSA)需要自定义两个方法

准备jsencryptlong.js(与encryption.js同级),RSA每次加密117bytes,需要辅助方法判断字符串截取位置

// 加密解密
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min';
//处理字符串过长加密报错 解决Message too long for RSA
const b64map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const b64pad = '=';
// 加密
JSEncrypt.prototype.encryptLong2 = function (string) {
  const k = this.getKey();
  try {
    let ct = '';
    //RSA每次加密117bytes,需要辅助方法判断字符串截取位置
    //1.获取字符串截取点
    const bytes = [];
    bytes.push(0);
    let byteNo = 0;
    let len, c;
    len = string.length;
    let temp = 0;
    for (let i = 0; i < len; i++) {
      c = string.charCodeAt(i);
      if (c >= 0x010000 && c <= 0x10ffff) {
        byteNo += 4;
      } else if (c >= 0x000800 && c <= 0x00ffff) {
        byteNo += 3;
      } else if (c >= 0x000080 && c <= 0x0007ff) {
        byteNo += 2;
      } else {
        byteNo += 1;
      }
      if (byteNo % 117 >= 114 || byteNo % 117 === 0) {
        if (byteNo - temp >= 114) {
          bytes.push(i);
          temp = byteNo;
        }
      }
    }
    //2.截取字符串并分段加密
    if (bytes.length > 1) {
      for (let i = 0; i < bytes.length - 1; i++) {
        let str;
        if (i === 0) {
          str = string.substring(0, bytes[i + 1] + 1);
        } else {
          str = string.substring(bytes[i] + 1, bytes[i + 1] + 1);
        }
        const t1 = k.encrypt(str);
        ct += addPreZero(t1, 256);
      }
      if (bytes[bytes.length - 1] !== string.length - 1) {
        const lastStr = string.substring(bytes[bytes.length - 1] + 1);
        const rsaStr = k.encrypt(lastStr);
        ct += addPreZero(rsaStr, 256);
      }
      //console.log("加密完的数据:"+ct);
      return hex2b64(ct);
    }
    const t = k.encrypt(string);
    const y = hex2b64(t);
    return y;
  } catch (ex) {
    return false;
  }
};
// 解密
JSEncrypt.prototype.decryptLong2 = function (string) {
  const k = this.getKey();
  // let maxLength = ((k.n.bitLength()+7)>>3);
  const MAX_DECRYPT_BLOCK = 128;
  try {
    let ct = '';
    let t1;
    let bufTmp;
    let hexTmp;
    const str = b64ToHex(string);
    const buf = hexToBytes(str);
    const inputLen = buf.length;
    //开始长度
    let offSet = 0;
    //结束长度
    let endOffSet = MAX_DECRYPT_BLOCK;

    //分段解密
    console.log(inputLen + '----' + offSet);
    while (inputLen - offSet > 0) {
      if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
        bufTmp = buf.slice(offSet, endOffSet);
        hexTmp = bytesToHex(bufTmp);
        t1 = k.decrypt(hexTmp);
        ct += t1;
      } else {
        bufTmp = buf.slice(offSet, inputLen);
        hexTmp = bytesToHex(bufTmp);
        t1 = k.decrypt(hexTmp);
        ct += t1;
      }
      offSet += MAX_DECRYPT_BLOCK;
      endOffSet += MAX_DECRYPT_BLOCK;
    }
    return ct;
  } catch (ex) {
    return false;
  }
};
// 加密辅助方法
function hex2b64(h) {
  let i;
  let c;
  let ret = '';
  for (i = 0; i + 3 <= h.length; i += 3) {
    c = parseInt(h.substring(i, i + 3), 16);
    ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63);
  }
  if (i + 1 === h.length) {
    c = parseInt(h.substring(i, i + 1), 16);
    ret += b64map.charAt(c << 2);
  } else if (i + 2 === h.length) {
    c = parseInt(h.substring(i, i + 2), 16);
    ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4);
  }
  while ((ret.length & 3) > 0) ret += b64pad;
  return ret;
}
function hexToBytes(hex) {
  for (var bytes = [], c = 0; c < hex.length; c += 2) bytes.push(parseInt(hex.substr(c, 2), 16));
  return bytes;
}
function bytesToHex(bytes) {
  for (var hex = [], i = 0; i < bytes.length; i++) {
    hex.push((bytes[i] >>> 4).toString(16));
    hex.push((bytes[i] & 0xf).toString(16));
  }
  return hex.join('');
}
function b64ToHex(str) {
  for (var i = 0, bin = atob(str.replace(/[ \r\n]+$/, '')), hex = []; i < bin.length; ++i) {
    let tmp = bin.charCodeAt(i).toString(16);
    if (tmp.length === 1) tmp = '0' + tmp;
    hex[hex.length] = tmp;
  }
  return hex.join('');
}
function addPreZero(num, length) {
  let t = (num + '').length,
    s = '';
  for (let i = 0; i < length - t; i++) {
    s += '0';
  }
  return s + num;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容