sign签名其实就是前后端约定一些规则,通过这些规则生成sign签名,在前端请求的时候将签名带到后端,签名会一直则通过,否则请求不通过。
这种方式也并非绝对安全,最近刚好在用就简单记录一下
前提
- 本文代码适用于
Vue + axios - 需要生成唯一标识,参考库
uuid-js - 需要进行MD5加密,参考库
js-md5
生成签名流程
- 前后端约定一组参数
appKey和appSecurity,值可以随便定义,如const appKey = 'appKey'和const appSecurity = 'appSecurity' - 拦截每一次请求,对请求的
config进行处理- 生成时间戳 timestamp
const timestamp = new Date().getTime() - 生成唯一标识 nonce
const nonce = UUID.create().toString() - 按约定规则处理参数:用一个数组接收所有参数,参数形式为
a=1,该数组格式如下['a=1', 'b=2', 'params={"demo":1,"test":""}'],【注意】当post请求Content-Type为application/json时,格式为params后边接上请求体中的字符串;只对Content-Type为application/json和application/x-www-form-urlencoded的请求的请求体数据进行签名,其他类型的请求体数据不参与签名 - 对参数数组进行去重处理,去重后将
appKey、timestamp、nonce参数加入参数数组 - 对参数数组进行排序,按照每一项的首字母ASCII从小到大排序
- 将参数数组中的每一项拼接成字符串,去除所有
=号,并在末尾加上appSecurity的值 - 对生成的字符串进行MD5加密处理,处理成32位全大写的加密串,这个就是sign
- 将
appKey、timestamp、nonce、sign加入请求头即可
- 生成时间戳 timestamp
参考代码
- 请求封装,拦截请求
http.js
import axios from 'axios'
import { createSign } from '....../sign.js'
axios.interceptors.request.use((config) => {
if (config.method === 'post' && config.headers['Content-Type'] === 'application/x-www-form-urlencoded') {
let data = qs.parse(config.data)
config.data = qs.stringify({
...data
})
}
config.params = {
...config.params,
// ...
}
config = createSign(config)
return config;
}, (error) => {
return Promise.reject(error.toString());
})
- 封装生成签名的逻辑
sign.js
import UUID from 'uuid-js'
import md5 from 'js-md5'
// 签名
const appKey = 'appKey'
const appSecurity = 'appSecurity'
// config 为拦击请求的config
export const createSign = (config) => {
const timestamp = new Date().getTime()
const nonce = UUID.create().toString()
let params = [].concat(handleUrl(config.url), handleParams(config.params), handleData(config))
// 去重复项
params = ['appKey=' + appKey, 'timestamp=' + timestamp, 'nonce=' + nonce, ...new Set(params)]
// 根据首字母排序
params = params.sort((a, b) => {
return (a + '').localeCompare(b + '')
})
// 拼装成字符串
params = params.join('').replace(/\=/g, '') + appSecurity
// console.log(params)
// md5加密
let sign = md5(params).toUpperCase()
// 将参数放入contentType
config.headers = {
...config.headers,
appKey,
timestamp,
nonce,
sign
}
return config
}
// 解析Url后面的参数
function handleUrl(url){
let index = url.indexOf('?')
if(index !== -1){
let str = url.substring(index + 1)
return str.split('&').filter(item => {
return item.split('=')[1]
})
}else{
return []
}
}
// 解析params对象
function handleParams(params){
if(Object.keys(params).length !== 0){
let res = []
for (const key in params) {
if (params[key]) {
res.push(key + '=' + params[key])
}
}
return res
}else{
return []
}
}
// 解析data对象
function handleData(config){
const data = config.data
const contentType = config.headers['Content-Type']
let res = []
if(config.method !== 'get'){
if(contentType === 'application/x-www-form-urlencoded'){
res = data.split('&').filter(item => {
return item.split('=')[1]
})
}
if(contentType === 'application/json'){
res.push('params=' + JSON.stringify(data))
}
}
return res
}
总结词
还是那句话,前端的东西都并非绝对的安全,只能通过各种手段加强安全性,sign签名这种方式是比较常用的一种手段,记一下方便开发。