前言
一般需要登录的系统我们可以使用Token进行身份认证,但是如果不用登录,我们该如何验证接口请求的合理行呢?
一般来说我们可以把参数进行一定方式的加密生成签名,后端直接校验参数和签名是否匹配,匹配才能正常请求。
详情
注意点
要注意以下几点
- 因为对象的属性的顺序可能不一致,所以我们在生成签名的时候要进行Key排序。
- 对象可能多层嵌套,我们要把对象拍平。
- 对象拍平的时候要注意数组的处理。
multipart/form-data请求要进行判断。
工具类
/assets/utils/sign_utils.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| import md5 from "blueimp-md5";
export function setHeaderSign(config) { let signObj = {};
const timestamp = Math.floor(Date.now() / 1000); Object.assign(signObj, { timestamp: timestamp }); config.headers["timestamp"] = timestamp;
const params = config.params; if (params) { Object.assign(signObj, params); }
const contentType = (config.headers["Content-Type"] || "").toLowerCase();
const isMultipart = contentType.includes("multipart/form-data");
const data = config.data; if (data && !isMultipart) { Object.assign(signObj, data); }
const signStr = getSignStr(signObj); let signature = md5(signStr); config.headers["signature"] = signature; }
export function getSignStr(params) { if (params) { params = flattenObject(params); const keys = Object.keys(params).sort(); return keys.map((key) => `${key}=${params[key]}`).join("&"); } else { return ""; } }
function flattenObject(obj, parentKey = "", result = {}) { for (const [key, value] of Object.entries(obj)) { const newKey = parentKey ? `${parentKey}.${key}` : key;
if (typeof value === "object" && value !== null && !Array.isArray(value)) { flattenObject(value, newKey, result); } else if (value && Array.isArray(value)) { for (let i = 0; i < value.length; i++) { const element = value[i]; if (typeof element === "object") { let tempKey = `${newKey}.${i}`; flattenObject(element, tempKey, result); } else { result[`${newKey}.${i}`] = element; } } } else { result[newKey] = value; } } return result; }
|
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import axios from "axios"; import { setHeaderSign } from "@/assets/utils/sign_utils";
const http = axios.create({ baseURL: window.baseURL, timeout: 1000 * 10, validateStatus: function (status) { return status >= 200 && status <= 500; }, headers: { "Content-Type": "application/json", }, });
http.interceptors.request.use( (config) => { setHeaderSign(config); return config; }, (err) => { return Promise.reject({ code: 1, msg: err.message || "请求失败", }); } );
export default http;
|
Tip
数据拍平
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function flattenObject(obj, parentKey = "", result = {}) { for (const [key, value] of Object.entries(obj)) { const newKey = parentKey ? `${parentKey}.${key}` : key;
if (typeof value === "object" && value !== null && !Array.isArray(value)) { flattenObject(value, newKey, result); } else if (value && Array.isArray(value)) { for (let i = 0; i < value.length; i++) { const element = value[i]; if (typeof element === "object") { let tempKey = `${newKey}.${i}`; flattenObject(element, tempKey, result); } else { result[`${newKey}.${i}`] = element; } } } else { result[newKey] = value; } } return result; }
|
数据是这样的
1 2 3 4 5 6 7 8 9 10 11 12
| const nestedObj = { a: 1, b: { c: 2, d: { e: 3 } }, f: [4, 5], g: [ { a: 1, b: 2 }, { c: 3, d: 4 }, ], };
const flattenedObj = flattenObject(nestedObj); console.log(flattenedObj);
|
结果是这样的
1 2 3 4 5 6 7 8 9 10 11
| { "a": 1, "b.c": 2, "b.d.e": 3, "f.0": 4, "f.1": 5, "g.0.a": 1, "g.0.b": 2, "g.1.c": 3, "g.1.d": 4 }
|