一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情。
写在前面
在进行接口请求时,我们希望携带的数据不希望是以明文的方式提交的,也就是需要对参数做一些混淆或者加密处理,后端拿到数据后再进行解密,得到真实数据。
其目的是为了保护数据的安全,以及提高被识破成明文的门槛。在例如用户登录的接口请求中,如果账号和密码是以明文传输的,会容易导致一些安全性问题,因此前端参数混淆非常有必要,这里分享一个自用的方案。
什么接口的参数需要做处理
从安全角度出发,这里一致认为,只要是对数据库有新增、修改、删除操作的,一律需要做混淆/加密,这一类接口,多为post
请求。
而对于get
请求,如果是规范化的接口,一般只是获取数据,不会对数据库有直接的操作,故不需要做参数处理。
当然还有post
请求,却不希望做参数处理的接口,需要特殊处理,以及在开发环境中,为了方便调试,也不需要做处理。
参数处理算法
这里使用的方案是,先将原始数据处理为query
形式的字符串,然后将其使用随机字符串作为密钥,参与Aes
加密,并截取特定的字符串,作为原始密钥,再进行一次Aes
加密,最后将原始密钥使用与后端约定好的公钥进行Rsa
加密处理,具体算法如下:
/**
* 加密请求数据
* @param {Object} rawData 原始数据
* @returns {data, key}
*/
export function encryptRequestData(rawData) {
// 字典排序并赋值
var result = {}, str = [], arr = Object.keys(rawData).sort();
arr.map(m => { result[m] = rawData[m] });
// 处理成 query 形式字符串
for (var p in result)
result.hasOwnProperty(p) && str.push(encodeURIComponent(p) + "=" + encodeURIComponent(result[p]));
result = str.join("&");
// 参与 Aes 加密的密钥,将处理后的字符串用 16 位随机码对称加密,再从第 3 位开始获取 16 位原始密钥
const rawKey = aesEncrypt(result, randomString(16)).substr(3, 16);
// 输出最后的加密参数
const data = aesEncrypt(JSON.stringify(rawData), rawKey);
const key = rsaEncrypt(rawKey);
return { data, key }
}
复制代码
参数处理时机
我们需要在请求拦截的时候对参数做混淆处理,这里只针对post
请求以及非本地环境,另外,在实例上还增加了uncrypt
来标识哪些接口可以不参与处理。
// 请求拦截
instance.interceptors.request.use(
config => {
config.headers.contentType = "application/x-www-form-urlencoded"
const token = local.get('token')
token && (config.headers.Authorization = token)
const { method, uncrypt = false, data = {} } = config;
(method === 'post' && !uncrypt && cfg.NODE_ENV === 'development') && (config.data = encryptRequestData(data));
return config
},
error => Promise.error(error)
)
复制代码