前端安全架构:自动鉴权与全局Token注入的深度实践
前端自动鉴权与全局 Token 注入的深度实践
“安全不是终点,而是每一次请求的起点。”
在现代 Web 应用中,接口安全和统一鉴权已成为前端架构设计的核心环节。尤其在微服务、API 网关盛行的今天,如何让前端自动、无感地完成鉴权,保障每一次数据交互的安全与合规,是每个前端工程师都绕不开的话题。
本文以实际项目中的 index.js 为例,结合关键代码,深入剖析如何在前端实现自动鉴权、全局 token 注入,并对方案的优缺点进行评价。
背景与目标
- 目标:所有前端 AJAX 请求都需带上动态获取的 access_token,且无需业务方手动处理。
- 难点:token 获取流程复杂,需多步加密、签名、指纹识别,且要保证首批请求不丢失。
“让安全成为默认,而不是负担。”
核心流程与关键实现
1. 获取 URL 参数
用于动态获取鉴权所需的 ckey 等参数。
function getUrlParams(variable) {
// ...existing code...
}
2. 鉴权流程封装为类
VerificationProcess 封装了整个鉴权流程,便于复用和扩展。
2.1 构造与配置
class VerificationProcess {
constructor(ckey) {
this.config = {
encryptKey: "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCt8e352hOSzQ+PEWC9PmulRfRdCJgsXMNFlgivAOCi/+49+GZakeYE1sfS0XhtNOlnUna0g220Y9WyQyHsotgGK12pexjLSVl2gvcWAbVIT6CDDUGMs6AFDxYdg6lgkdUup+sizgE4wIDAQAB",
globalKey: ["\x34", "\x43", "\x38", "\x39", "\x37", "\x38", "\x33","\x35", "\x44", "\x35"],
evenKey: ["2", "9", "6", "B", "F", "6","9", "1", "B", "A"],
oddKey: ["A", "5", "F", "6", "5", "6", "4", "3", "D", "A"]
};
this.fingerPrint = "";
this.requestQueue = [];
this.ckey = ckey || "";
this.verificationRes = { verificationCode: "", date: "" };
this.userInfo = "";
this.signNature = "";
this.access_token = "";
}
// ...其余方法见下文...
}
2.2 获取浏览器指纹
利用 fingerprintjs2 生成唯一指纹,作为鉴权因子。
async getFingerPrint() {
var fp = window.Fingerprint2;
return new Promise(function (resolve, reject) {
fp.get(function (components) {
var values = components.map(function (component) {
return component.value;
});
var murmur = fp.x64hash128(values.join(""), 31);
if (components) {
resolve(murmur);
} else {
reject("");
}
});
});
}
2.3 获取验证码与加密信息
- 先用指纹请求验证码
- 再用公钥加密、异或加密、Base64 编码等多重加密生成 userInfo
async getVerificationAndCode() {
if (!this.fingerPrint) {
console.error("VerificationProcess未获取到fingerprint");
return;
}
const res = await axios.get(
"https://b2b-api.10jqka.com.cn/gateway/service-mana/encryption/sendVerificationCode",
{ params: { fingerPrint: this.fingerPrint } }
);
Object.assign(this.verificationRes, {
verificationCode: res.data.data.verificationCode || "",
date: res.data.data.date || "",
});
}
getUserInfo() {
if (!this.ckey || !this.fingerPrint || !this.verificationRes.verificationCode) {
console.error("VerificationProcess未获取到ckey或fingerprint或verificationCode");
return;
}
var data =
"verificationCode=" + this.verificationRes.verificationCode +
";ckey=" + this.ckey +
";fingerPrint=" + this.fingerPrint;
var encrypt = new JSEncrypt();
encrypt.setPublicKey(this.config.encryptKey);
var encryptCode = encrypt.encrypt(data);
var xorEncryptCode = this.xorEncode(
encryptCode,
this.config.globalKey,
this.config.evenKey,
this.config.oddKey
);
this.userInfo = xorEncryptCode;
}
2.4 生成签名
getSignNature(randomNum) {
var data =
"verificationCode=" + this.verificationRes.verificationCode +
";date=" + this.verificationRes.date +
";randomCode=" + randomNum;
this.signNature = CryptoJS.SHA1(data).toString();
}
2.5 获取 access_token
async getToken() {
if (!this.fingerPrint) {
this.fingerPrint = await this.getFingerPrint();
}
await this.getVerificationAndCode();
this.setCKey();
this.getUserInfo();
var randomNum = this.random16();
this.getSignNature(randomNum);
var requestData = new URLSearchParams();
requestData.append("userInfo", this.userInfo);
requestData.append("signature", this.signNature);
requestData.append("randomCode", randomNum);
requestData.append("date", this.verificationRes.date);
var res = await axios({
method: "post",
url: "https://b2b-api.10jqka.com.cn/gateway/service-mana/encryption/requestToken",
headers: {
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
},
data: requestData,
});
var access_token = res.data.data.access_token;
this.access_token = access_token;
// ...后续全局注入见下文...
}
3. 全局劫持 XMLHttpRequest,自动注入 token
3.1 劫持 send 方法,队列化所有请求
var origSend = xhrProto.send;
xhrProto.send = function () {
// 除了鉴权相关接口,其他请求全部暂存
if (
!this.requestURL ||
(this.requestURL &&
!this.requestURL.includes("/service-mana/encryption/sendVerificationCode") &&
!this.requestURL.includes("/service-mana/encryption/requestToken"))
) {
verificationProcess.addRequestQueue({
_this: this,
params: arguments,
});
} else {
return origSend.apply(this, arguments);
}
};
3.2 token 获取后,自动补发所有请求
xhrProto.send = function () {
this.setRequestHeader("Open-Authorization", "Bearer " + access_token);
return origSend.apply(this, arguments);
};
// 补发队列
this.requestQueue.forEach(function (item) {
item._this.setRequestHeader("Open-Authorization", "Bearer " + access_token);
origSend.apply(item._this, item.params);
});
this.requestQueue = [];
本方案通过前端自动鉴权与全局 token 注入,极大提升了接口安全性和开发效率。它让安全成为前端的“默认能力”,而不是额外负担。对于需要统一鉴权、token 动态获取的前端项目,这一模式值得深入学习和推广。

浙公网安备 33010602011771号