通过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,
})
组件内:正常传递与获取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;
}