sign签名前端实践

4,336 阅读2分钟

sign签名其实就是前后端约定一些规则,通过这些规则生成sign签名,在前端请求的时候将签名带到后端,签名会一直则通过,否则请求不通过。
这种方式也并非绝对安全,最近刚好在用就简单记录一下

前提

  • 本文代码适用于 Vue + axios
  • 需要生成唯一标识,参考库 uuid-js
  • 需要进行MD5加密,参考库 js-md5

生成签名流程

  1. 前后端约定一组参数 appKeyappSecurity,值可以随便定义,如 const appKey = 'appKey'const appSecurity = 'appSecurity'
  2. 拦截每一次请求,对请求的 config 进行处理
    1. 生成时间戳 timestamp const timestamp = new Date().getTime()
    2. 生成唯一标识 nonce const nonce = UUID.create().toString()
    3. 按约定规则处理参数:用一个数组接收所有参数,参数形式为 a=1,该数组格式如下 ['a=1', 'b=2', 'params={"demo":1,"test":""}'],【注意】当post请求 Content-Typeapplication/json时,格式为 params 后边接上请求体中的字符串;只对 Content-Typeapplication/jsonapplication/x-www-form-urlencoded 的请求的请求体数据进行签名,其他类型的请求体数据不参与签名
    4. 对参数数组进行去重处理,去重后将 appKeytimestampnonce参数加入参数数组
    5. 对参数数组进行排序,按照每一项的首字母ASCII从小到大排序
    6. 将参数数组中的每一项拼接成字符串,去除所有 = 号,并在末尾加上 appSecurity的值
    7. 对生成的字符串进行MD5加密处理,处理成32位全大写的加密串,这个就是sign
    8. appKeytimestampnoncesign加入请求头即可

参考代码

  • 请求封装,拦截请求 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签名这种方式是比较常用的一种手段,记一下方便开发。