在数据请求和接收时,因为要兼顾系统安全测试,要对接口部分数据或者整个请求体进行加密解密,这时候根据不同的安全要求需要前端使用不同的方法进行实现。
加密算法常用的有三种,分别为散列算法,对称算法,非对称算法。也对应着前端常使用的三种方法md5,aes和sm2。
- 散列算法是不可逆的,安全姓极高但灵活性不够。常用于对身份信息的验证,例如登录。
- 对称算法前后端使用同一个密钥,加密速度快,但安全性低于非对称算法。可使用于对安全性要求不高但有需要进行一定的加密的系统。
- 非对称算法有私钥和公钥的区分,安全性高,但加密和解密的速度较慢。用于对安全性要求严格的系统。
- 但为了方便以下案例都以登录为例。
1.md5
1.1安装
npm install js-md5
1.2引用
import md5 from 'js-md5'
1.3使用
axios.post('xxx/login', {
username: this.username,
password: md5(this.password)
})
2.aes
2.1安装
npm install crypto-js
2.2引用
import CryptoJS from 'crypto-js'
2.3封装
export default {
// 加密
encrypt(word) {
let stringkey = "5C3TyE5kSHeE2e5a";
var key = CryptoJS.enc.Utf8.parse(stringkey);
var srcs = CryptoJS.enc.Utf8.parse(word);
var resultByte = CryptoJS.AES.encrypt(srcs, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
});
return resultByte.toString();
},
// 解密
decrypt(word) {
let stringkey = "5C3TyE5kSHeE2e5a";
var key = CryptoJS.enc.Utf8.parse(stringkeys);
var resultByte = CryptoJS.AES.decrypt(word, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
});
return CryptoJS.enc.Utf8.stringify(resultByte).toString();
},
}
2.4使用
import AES from '@/utils/common/crypto.js'
let json = {
username: this.username,
password: this.password,
};
let param = AES.encrypt(JSON.stringify(json));
axios.post('xxx/login', { requestData: param })
3.sm2
3.1安装
npm install --save sm-crypto
3.2引用
const sm2 = require('sm-crypto').sm2 // 获取sm2对象
3.3封装
//为0时在加密的串前加上04,后端才能解密成功,同样后端加密后也会返回带04的加密串给前端,cipherMode为1的话必须去掉04才能解密成功
const cipherMode = 0 // 选择加密策略,1 - C1C3C2,0 - C1C2C3,默认为1
const sysPublicKey = '04A1AEEB6A3A2DD87A9439AAC2E90CF5EC41CE85C6BA98349BE52344951DAE44BCE78FD2A914030F95A7A418D094B9F659BF120488B12DEAFEE58A27D9A4B38DE1' // 系统后台公钥
const uiPrivateKey = '00E51DC7364DE2AB9061677FDFA181CC3438761DEA9E67FC7A19890EC23251DF64' // 前端UI私钥
export default {
// 加密
doEncrypt(data){
//加'04'
return '04' + sm2.doEncrypt(data, sysPublicKey, cipherMode)
},
// 解密
doDecrypt(data){
dataHex = data.substring(2).toLocaleLowerCase()
return sm2.doDecrypt(dataHex, uiPrivateKey, cipherMode)
},
// 签名(如果需要)
doSignature(msg){
return sm2.doSignature(msg, uiPrivateKey, { hash:true, der:true });
}
}
3.4使用
import SM from '@/utils/common/sm2.js'
let json = {
username: this.username,
password: this.password,
};
let param = SM.doEncrypt(JSON.stringify(json));
//请求头加入接口签名(如果需要)
let timestamp = new Date().getTime().toString();
let sign = SM.doSignature(timestamp);
axios.post('xxx/login', { requestData: param },{headers:{'timestamp':timestamp,'sign':sign}})
当然以上都为简单使用,针对灵活性高,数据格式复杂例如formdata,file等格式等的请求体进行加密和解密还要在此基础上增加封装,这里贴出一个案例供大家思考。
const sm2 = require('sm-crypto').sm2 // 获取sm2对象
const cipherMode = 0 // 选择加密策略,1 - C1C3C2,0 - C1C2C3,默认为1
const sysPublicKey = '04A1AEEB6A3A2DD87A9439AAC2E90CF5EC41CE85C6BA98349BE52344951DAE44BCE78FD2A914030F95A7A418D094B9F659BF120488B12DEAFEE58A27D9A4B38DE1' // 系统后台公钥
const uiPrivateKey = '00E51DC7364DE2AB9061677FDFA181CC3438761DEA9E67FC7A19890EC23251DF64' // 前端UI私钥
// 开启全局加密
const enableGlobal = false;
// 必须加密请求体的请求
const mustEncryptRequestBodyUrls = [
// '/configuration/taskManage/save','/oauth/login'
]
// 不加密请求体的请求
const forbiddenEncryptRequestBodyUrls = []
/**
* 是否为必须加密请求体的请求
* @param {*} url
* @returns
*/
function isMustEncryptRequestBodyUrls(url){
return mustEncryptRequestBodyUrls.filter(element => element == url).length > 0
}
/**
* 是否为不加密请求体的请求
* @param {*} url
* @returns
*/
function isForbiddenEncryptRequestBodyUrls(url){
return forbiddenEncryptRequestBodyUrls.filter(element => element == url).length > 0
}
/**
* SM2加密string数据
* @param {string} data 原始数据
* @returns {string} 加密后数据
*/
function getSm2DataHexByString(data) {
if (data && (typeof data === 'string') && data.constructor === String) {
return '04' + sm2.doEncrypt(data, sysPublicKey, cipherMode)
}
return null
}
/**
* SM2加密object数据
* @param {Object} data 原始数据
* @returns {string} 加密后数据
*/
function getSm2DataHexByObject(url,data) {
if (data) {
if(data instanceof FormData) {
if(url === '/configuration/cnfTaskDeliver/add') {
return '04' + sm2.doEncrypt(JSON.stringify(data), sysPublicKey, cipherMode)
} else {
let formData = new FormData();
data.forEach((value,key)=>{
if(value instanceof File) {
formData.append(key,value);
} else {
if(typeof(value) === 'string') {
if(value!==''&&value!==null) {
formData.append(key,'04' + sm2.doEncrypt(value, sysPublicKey, cipherMode));
}
} else {
if(value!==''&&value!==null) {
formData.append(key,'04' + sm2.doEncrypt(JSON.stringify(value), sysPublicKey, cipherMode));
}
}
}
})
return formData;
}
/* if(validType("[object File]",data)) {
return '04' + sm2.doEncrypt(JSON.stringify(data), sysPublicKey, cipherMode)
} else {
let formData = new FormData();
data.forEach((value,key)=>{
if(value instanceof File) {
formData.append(key,value);
} else {
if(typeof(value) === 'string') {
if(value!==''&&value!==null) {
formData.append(key,'04' + sm2.doEncrypt(value, sysPublicKey, cipherMode));
}
} else {
if(value!==''&&value!==null) {
formData.append(key,'04' + sm2.doEncrypt(JSON.stringify(value), sysPublicKey, cipherMode));
}
}
}
})
return formData;
} */
} else {
return '04' + sm2.doEncrypt(JSON.stringify(data), sysPublicKey, cipherMode)
}
}
return null
}
/**
* SM2解密数据
* @param {string} dataHex 原始加密数据
* @returns {string} 解密后数据
*/
function getSm2DataByString(dataHex) {
if (dataHex && (typeof dataHex === 'string') && dataHex.constructor === String) {
dataHex = dataHex.substring(2).toLocaleLowerCase()
return sm2.doDecrypt(dataHex, uiPrivateKey, cipherMode)
}
}
/**
* SM2签名
* @param {string} msg
* @returns
*/
function doSignature(msg){
return sm2.doSignature(msg, uiPrivateKey, { hash:true, der:true });
}
/* 判断data是不是formData类型 */
function validType(val,formData) {
const types = [];
for(let key of formData.keys()) {
types.push(Object.prototype.toString.call(formData.get(key)));
}
return types.every(item => item === Object.prototype.toString.call(val));
}
export default {
enableGlobal,
mustEncryptRequestBodyUrls,
forbiddenEncryptRequestBodyUrls,
isMustEncryptRequestBodyUrls,
isForbiddenEncryptRequestBodyUrls,
getSm2DataHexByString,
getSm2DataHexByObject,
getSm2DataByString,
doSignature
}