fix: 前端请求头增加浏览器指纹

This commit is contained in:
Gsh
2025-06-28 18:44:10 +08:00
parent 1d7a2013e3
commit 5383d2d40e
3 changed files with 96 additions and 39 deletions

View File

@@ -30,6 +30,7 @@
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@fingerprintjs/fingerprintjs": "^3.4.2",
"@floating-ui/core": "^1.7.1",
"@floating-ui/dom": "^1.7.1",
"@floating-ui/vue": "^1.1.6",
@@ -37,6 +38,7 @@
"@vueuse/core": "^13.3.0",
"@vueuse/integrations": "^13.3.0",
"element-plus": "^2.9.11",
"fingerprintjs": "^0.5.3",
"hook-fetch": "^1.1.3",
"nprogress": "^0.2.0",
"pinia": "^3.0.3",
@@ -52,6 +54,7 @@
"@antfu/eslint-config": "^4.13.3",
"@changesets/cli": "^2.29.4",
"@commitlint/config-conventional": "^19.8.1",
"@types/fingerprintjs__fingerprintjs": "^3.0.2",
"@vitejs/plugin-vue": "^5.2.4",
"@vue/tsconfig": "^0.7.0",
"commitlint": "^19.8.1",

View File

@@ -11,6 +11,9 @@ importers:
'@element-plus/icons-vue':
specifier: ^2.3.1
version: 2.3.1(vue@3.5.16(typescript@5.8.3))
'@fingerprintjs/fingerprintjs':
specifier: ^3.4.2
version: 3.4.2
'@floating-ui/core':
specifier: ^1.7.1
version: 1.7.1
@@ -32,6 +35,9 @@ importers:
element-plus:
specifier: ^2.9.11
version: 2.9.11(vue@3.5.16(typescript@5.8.3))
fingerprintjs:
specifier: ^0.5.3
version: 0.5.3
hook-fetch:
specifier: ^1.1.3
version: 1.1.3(react@19.1.0)(typescript-api-pro@0.0.7)(vue@3.5.16(typescript@5.8.3))
@@ -72,6 +78,9 @@ importers:
'@commitlint/config-conventional':
specifier: ^19.8.1
version: 19.8.1
'@types/fingerprintjs__fingerprintjs':
specifier: ^3.0.2
version: 3.0.2
'@vitejs/plugin-vue':
specifier: ^5.2.4
version: 5.2.4(vite@6.3.5(@types/node@22.15.30)(jiti@2.4.2)(sass-embedded@1.89.1)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3))
@@ -110,7 +119,7 @@ importers:
version: 6.0.0(stylelint@16.20.0(typescript@5.8.3))
stylelint-config-recommended-scss:
specifier: ^15.0.1
version: 15.0.1(postcss@8.5.4)(stylelint@16.20.0(typescript@5.8.3))
version: 15.0.1(postcss@5.2.18)(stylelint@16.20.0(typescript@5.8.3))
stylelint-config-recommended-vue:
specifier: ^1.6.0
version: 1.6.0(postcss-html@1.8.0)(stylelint@16.20.0(typescript@5.8.3))
@@ -119,7 +128,7 @@ importers:
version: 38.0.0(stylelint@16.20.0(typescript@5.8.3))
stylelint-config-standard-scss:
specifier: ^15.0.1
version: 15.0.1(postcss@8.5.4)(stylelint@16.20.0(typescript@5.8.3))
version: 15.0.1(postcss@5.2.18)(stylelint@16.20.0(typescript@5.8.3))
typescript:
specifier: ~5.8.3
version: 5.8.3
@@ -128,7 +137,7 @@ importers:
version: 0.0.7
unocss:
specifier: 66.1.3
version: 66.1.3(postcss@8.5.4)(vite@6.3.5(@types/node@22.15.30)(jiti@2.4.2)(sass-embedded@1.89.1)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3))
version: 66.1.3(postcss@5.2.18)(vite@6.3.5(@types/node@22.15.30)(jiti@2.4.2)(sass-embedded@1.89.1)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3))
unplugin-auto-import:
specifier: ^19.3.0
version: 19.3.0(@nuxt/kit@3.17.5)(@vueuse/core@13.3.0(vue@3.5.16(typescript@5.8.3)))
@@ -648,6 +657,12 @@ packages:
resolution: {integrity: sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@fingerprintjs/fingerprintjs@3.4.2':
resolution: {integrity: sha512-3Ncze6JsJpB7BpYhqIgvBpfvEX1jsEKrad5hQBpyRQxtoAp6hx3+R46zqfsuQG4D9egQZ+xftQ0u4LPFMB7Wmg==}
'@fingerprintjs/fingerprintjs@4.6.2':
resolution: {integrity: sha512-g8mXuqcFKbgH2CZKwPfVtsUJDHyvcgIABQI7Y0tzWEFXpGxJaXuAuzlifT2oTakjDBLTK4Gaa9/5PERDhqUjtw==}
'@floating-ui/core@1.7.1':
resolution: {integrity: sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==}
@@ -976,6 +991,10 @@ packages:
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
'@types/fingerprintjs__fingerprintjs@3.0.2':
resolution: {integrity: sha512-jjQ03OYO/7qKtI/vOKZ9hSXtHxHWJkwsMSMSFszY+Y+JcOnjbRlvodk+zxXSxKNQweiDTJo4PfsXtOJLC9+auQ==}
deprecated: This is a stub types definition. @fingerprintjs/fingerprintjs provides its own type definitions, so you do not need this installed.
'@types/geojson@7946.0.16':
resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
@@ -2575,6 +2594,10 @@ packages:
resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==}
engines: {node: '>=18'}
fingerprintjs@0.5.3:
resolution: {integrity: sha512-LPW+iJ9yaV+FIXbmPkbyp7n6UoxV1euVozieaq3ckuvPEAyJRRB0JxbBsShHh5h1jc9yn/EZ95EEb78109zuKg==}
deprecated: Package has been renamed to @fingerprintjs/fingerprintjs. Install @fingerprintjs/fingerprintjs to get updates.
flat-cache@4.0.1:
resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
engines: {node: '>=16'}
@@ -5555,6 +5578,14 @@ snapshots:
'@eslint/core': 0.14.0
levn: 0.4.1
'@fingerprintjs/fingerprintjs@3.4.2':
dependencies:
tslib: 2.8.1
'@fingerprintjs/fingerprintjs@4.6.2':
dependencies:
tslib: 2.8.1
'@floating-ui/core@1.7.1':
dependencies:
'@floating-ui/utils': 0.2.9
@@ -5916,6 +5947,10 @@ snapshots:
'@types/estree@1.0.8': {}
'@types/fingerprintjs__fingerprintjs@3.0.2':
dependencies:
'@fingerprintjs/fingerprintjs': 4.6.2
'@types/geojson@7946.0.16': {}
'@types/json-schema@7.0.15': {}
@@ -6091,13 +6126,13 @@ snapshots:
transitivePeerDependencies:
- vue
'@unocss/postcss@66.1.3(postcss@8.5.4)':
'@unocss/postcss@66.1.3(postcss@5.2.18)':
dependencies:
'@unocss/config': 66.1.3
'@unocss/core': 66.1.3
'@unocss/rule-utils': 66.1.3
css-tree: 3.1.0
postcss: 8.5.4
postcss: 5.2.18
tinyglobby: 0.2.14
'@unocss/preset-attributify@66.1.3':
@@ -7805,6 +7840,8 @@ snapshots:
path-exists: 5.0.0
unicorn-magic: 0.1.0
fingerprintjs@0.5.3: {}
flat-cache@4.0.1:
dependencies:
flatted: 3.3.3
@@ -9213,9 +9250,9 @@ snapshots:
dependencies:
postcss: 8.5.4
postcss-scss@4.0.9(postcss@8.5.4):
postcss-scss@4.0.9(postcss@5.2.18):
dependencies:
postcss: 8.5.4
postcss: 5.2.18
postcss-selector-parser@6.1.2:
dependencies:
@@ -9806,14 +9843,14 @@ snapshots:
stylelint: 16.20.0(typescript@5.8.3)
stylelint-order: 6.0.4(stylelint@16.20.0(typescript@5.8.3))
stylelint-config-recommended-scss@15.0.1(postcss@8.5.4)(stylelint@16.20.0(typescript@5.8.3)):
stylelint-config-recommended-scss@15.0.1(postcss@5.2.18)(stylelint@16.20.0(typescript@5.8.3)):
dependencies:
postcss-scss: 4.0.9(postcss@8.5.4)
postcss-scss: 4.0.9(postcss@5.2.18)
stylelint: 16.20.0(typescript@5.8.3)
stylelint-config-recommended: 16.0.0(stylelint@16.20.0(typescript@5.8.3))
stylelint-scss: 6.12.0(stylelint@16.20.0(typescript@5.8.3))
optionalDependencies:
postcss: 8.5.4
postcss: 5.2.18
stylelint-config-recommended-vue@1.6.0(postcss-html@1.8.0)(stylelint@16.20.0(typescript@5.8.3)):
dependencies:
@@ -9827,13 +9864,13 @@ snapshots:
dependencies:
stylelint: 16.20.0(typescript@5.8.3)
stylelint-config-standard-scss@15.0.1(postcss@8.5.4)(stylelint@16.20.0(typescript@5.8.3)):
stylelint-config-standard-scss@15.0.1(postcss@5.2.18)(stylelint@16.20.0(typescript@5.8.3)):
dependencies:
stylelint: 16.20.0(typescript@5.8.3)
stylelint-config-recommended-scss: 15.0.1(postcss@8.5.4)(stylelint@16.20.0(typescript@5.8.3))
stylelint-config-recommended-scss: 15.0.1(postcss@5.2.18)(stylelint@16.20.0(typescript@5.8.3))
stylelint-config-standard: 38.0.0(stylelint@16.20.0(typescript@5.8.3))
optionalDependencies:
postcss: 8.5.4
postcss: 5.2.18
stylelint-config-standard@38.0.0(stylelint@16.20.0(typescript@5.8.3)):
dependencies:
@@ -10189,12 +10226,12 @@ snapshots:
universalify@2.0.1: {}
unocss@66.1.3(postcss@8.5.4)(vite@6.3.5(@types/node@22.15.30)(jiti@2.4.2)(sass-embedded@1.89.1)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3)):
unocss@66.1.3(postcss@5.2.18)(vite@6.3.5(@types/node@22.15.30)(jiti@2.4.2)(sass-embedded@1.89.1)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3)):
dependencies:
'@unocss/astro': 66.1.3(vite@6.3.5(@types/node@22.15.30)(jiti@2.4.2)(sass-embedded@1.89.1)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3))
'@unocss/cli': 66.1.3
'@unocss/core': 66.1.3
'@unocss/postcss': 66.1.3(postcss@8.5.4)
'@unocss/postcss': 66.1.3(postcss@5.2.18)
'@unocss/preset-attributify': 66.1.3
'@unocss/preset-icons': 66.1.3
'@unocss/preset-mini': 66.1.3

View File

@@ -1,10 +1,21 @@
import type { HookFetchPlugin } from 'hook-fetch';
import FingerprintJS from '@fingerprintjs/fingerprintjs'; // 新增指纹库
import { ElMessage } from 'element-plus';
import hookFetch from 'hook-fetch';
import { sseTextDecoderPlugin } from 'hook-fetch/plugins';
import router from '@/routers';
import { useUserStore } from '@/stores';
// 标准响应格式
// 初始化指纹(单例模式)
const fpPromise = FingerprintJS.load();
// 获取浏览器指纹(缓存结果)
async function getFingerprint(): Promise<string> {
const fp = await fpPromise;
const { visitorId } = await fp.get();
return visitorId;
}
// 标准响应格式
interface BaseResponse<T = any> {
code: number;
@@ -12,13 +23,12 @@ interface BaseResponse<T = any> {
msg: string;
}
// 扩展请求函数类型声明
declare module 'hook-fetch' {
interface HookFetchDefaults {
// 允许响应是裸数据(自动会被插件包装)
response: any;
}
}
export const request = hookFetch.create<BaseResponse>({
baseURL: import.meta.env.VITE_WEB_BASE_API,
headers: {
@@ -26,20 +36,32 @@ export const request = hookFetch.create<BaseResponse>({
},
plugins: [
sseTextDecoderPlugin({ json: true, prefix: 'data:' }),
{
name: 'fingerprint-plugin', // 新增指纹插件
beforeRequest: async (config) => {
try {
const fingerprint = await getFingerprint();
config.headers = new Headers(config.headers);
config.headers.set('X-Fingerprint', fingerprint);
}
catch (error) {
console.error('Failed to generate fingerprint:', error);
}
return config;
},
},
{
name: 'adapt-array-response',
afterResponse: async (response) => {
// 已经是标准格式(包含 code 字段)
if (typeof response.result?.code === 'number') {
return response;
}
// 非标准格式 → 包装为标准格式
return {
...response,
result: {
code: 200, // 默认成功码
data: response.result, // 原始数据放入 data
msg: 'success', // 默认消息
code: 200,
data: response.result,
msg: 'success',
},
};
},
@@ -47,35 +69,33 @@ export const request = hookFetch.create<BaseResponse>({
],
});
// JWT插件保持原有逻辑
function jwtPlugin(): HookFetchPlugin<BaseResponse> {
const userStore = useUserStore();
return {
name: 'jwt',
beforeRequest: async (config) => {
config.headers = new Headers(config.headers);
config.headers.set('authorization', `Bearer ${userStore.token}`);
if (userStore.token) {
config.headers = new Headers(config.headers);
config.headers.set('authorization', `Bearer ${userStore.token}`);
}
return config;
},
afterResponse: async (response) => {
// console.log(response);
if (response.result?.code === 200) {
if (response.result?.code === 200)
return response;
}
// 处理403逻辑
if (response.result?.code === 403) {
// 跳转到403页面确保路由已配置
router.replace({
name: '403',
});
router.replace({ name: '403' });
ElMessage.error(response.result?.msg);
return Promise.reject(response);
}
// 处理401逻辑
if (response.result?.code === 401) {
// 如果没有权限,退出,且弹框提示登录
userStore.logout();
userStore.openLoginDialog();
}
ElMessage.error(response.result?.msg);
return Promise.reject(response);
},
@@ -84,12 +104,9 @@ function jwtPlugin(): HookFetchPlugin<BaseResponse> {
request.use(jwtPlugin());
// 导出方法(保持原有)
export const post = request.post;
export const get = request.get;
export const put = request.put;
export const del = request.delete;
export default request;