From d9714b10f3ed7e2e615623f76b271f1a0f028430 Mon Sep 17 00:00:00 2001 From: lijun Date: Wed, 1 Oct 2025 14:27:41 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B5=8F=E8=A7=88=E5=99=A8?= =?UTF-8?q?=E6=8C=87=E7=BA=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 14 +++++++++++ package.json | 1 + src/utils/fingerprint.ts | 47 ++++++++++++++++++++++++++++++++++++ src/views/pages/login.vue | 14 +++++++++-- src/views/pages/register.vue | 7 +++++- yarn.lock | 12 +++++++++ 6 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 src/utils/fingerprint.ts diff --git a/package-lock.json b/package-lock.json index 859a880..751afe8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@codemirror/theme-one-dark": "^6.1.3", "@codemirror/view": "^6.38.1", "@element-plus/icons-vue": "*", + "@fingerprintjs/fingerprintjs": "^4.6.2", "@microsoft/fetch-event-source": "^2.0.1", "@wangeditor/editor": "^5.1.23", "@wangeditor/editor-for-vue": "^5.1.12", @@ -620,6 +621,19 @@ "node": ">=12" } }, + "node_modules/@fingerprintjs/fingerprintjs": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs/-/fingerprintjs-4.6.2.tgz", + "integrity": "sha512-g8mXuqcFKbgH2CZKwPfVtsUJDHyvcgIABQI7Y0tzWEFXpGxJaXuAuzlifT2oTakjDBLTK4Gaa9/5PERDhqUjtw==", + "dependencies": { + "tslib": "^2.4.1" + } + }, + "node_modules/@fingerprintjs/fingerprintjs/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/@floating-ui/core": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", diff --git a/package.json b/package.json index a55f1a9..5b43344 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@codemirror/theme-one-dark": "^6.1.3", "@codemirror/view": "^6.38.1", "@element-plus/icons-vue": "*", + "@fingerprintjs/fingerprintjs": "^4.6.2", "@microsoft/fetch-event-source": "^2.0.1", "@wangeditor/editor": "^5.1.23", "@wangeditor/editor-for-vue": "^5.1.12", diff --git a/src/utils/fingerprint.ts b/src/utils/fingerprint.ts new file mode 100644 index 0000000..1f97ded --- /dev/null +++ b/src/utils/fingerprint.ts @@ -0,0 +1,47 @@ +import FingerprintJS from '@fingerprintjs/fingerprintjs'; + +// 初始化指纹识别器 +let fpPromise; + +function initFingerprint() { + if (!fpPromise) { + // 加载指纹识别库 + fpPromise = FingerprintJS.load(); + } + return fpPromise; +} + +// 获取浏览器指纹信息 +export async function getBrowserFingerprint() { + try { + const fp = await initFingerprint(); + // 获取指纹结果 + const result = await fp.get(); + + // 返回核心信息:指纹ID和详细特征 + return { + fingerprint: result.visitorId, // 唯一指纹ID + components: result.components, // 组成指纹的各特征详情 + confidence: result.confidence.score // 置信度分数(0-1) + }; + } catch (error) { + console.error('获取浏览器指纹失败:', error); + return null; + } +} + +// 持久化存储指纹(结合localStorage提高稳定性) +export async function getStoredFingerprint() { + // 尝试从本地存储获取 + const stored = localStorage.getItem('browser_fingerprint'); + if (stored) { + return JSON.parse(stored); + } + + // 生成新指纹并存储 + const fingerprint = await getBrowserFingerprint(); + if (fingerprint) { + localStorage.setItem('browser_fingerprint', JSON.stringify(fingerprint)); + } + return fingerprint; +} diff --git a/src/views/pages/login.vue b/src/views/pages/login.vue index 17e920a..976e506 100644 --- a/src/views/pages/login.vue +++ b/src/views/pages/login.vue @@ -119,10 +119,12 @@ import { ElMessage } from "element-plus"; import { loginService } from "@/api/user"; import { GetUserInfoService } from "@/api/user"; import { UserToken } from "@/types/user"; +import { getBrowserFingerprint,getStoredFingerprint } from "@/utils/fingerprint"; import { usePermissStore } from "@/store/permiss"; import Cookies from 'js-cookie'; import {getThirdPartyUUID,getThirdPartyLoginStatus,getThirdPartyLoginUrl,sendLoginCode, loginByCode} from "@/api/user"; -import { time } from "console"; +import { log, time } from "console"; +import { pa } from "element-plus/es/locale"; // 从本地存储获取登录参数 const lgStr = localStorage.getItem("login-param"); const defParam = lgStr ? JSON.parse(lgStr) : null; @@ -139,6 +141,7 @@ const route = useRoute(); const param = reactive({ username: defParam ? defParam.username : "", password: defParam ? defParam.password : "", + fingerprint: '' // 浏览器指纹 }); //验证码登录表单数据 @@ -146,6 +149,7 @@ const code_login_form = ref({ email: '', code: '', login_type: 1, // 1: 邮箱验证码登录 2: 手机验证码登录 + fingerprint: '' // 浏览器指纹 }); // 表单验证规则 @@ -227,10 +231,16 @@ var loginData = ref({ email: "", password: "", ip: "", + fingerprint: '' // 浏览器指纹 }); const querySite =ref(""); -onMounted(() => { +onMounted(async () => { + let fp = await getStoredFingerprint(); + param.fingerprint = fp.fingerprint; + loginData.value.fingerprint = fp.fingerprint; + code_login_form.value.fingerprint = fp.fingerprint; + console.log("fp",fp.fingerprint); // 获取query参数 const queryParams = route.query; console.log('Received query parameters:', queryParams); diff --git a/src/views/pages/register.vue b/src/views/pages/register.vue index 62d17b6..10274ad 100644 --- a/src/views/pages/register.vue +++ b/src/views/pages/register.vue @@ -69,6 +69,7 @@ import { ElMessage } from 'element-plus'; import { registerService,getRigisterEmailCode } from "@/api/user"; import {GetUserInfoService} from "@/api/user"; import { usePermissStore } from "@/store/permiss"; +import { getStoredFingerprint } from '@/utils/fingerprint'; const router = useRouter(); const permiss = usePermissStore(); @@ -78,7 +79,8 @@ const param = reactive({ password: '', repassword: '', email: '', - code: '' + code: '', + fingerprint: '' // 浏览器指纹 }); const registerData = ref({ @@ -86,6 +88,7 @@ const registerData = ref({ email: "", password: "", repassword: "", + fingerprint: '' // 浏览器指纹 }); const buttonText = ref('发送验证码'); const countdown = ref(60); // 初始倒计时时间 @@ -138,7 +141,9 @@ const getMyUserInfo = async (id) => { }; //注册接口调用 const onRegister = async () => { + let fp = await getStoredFingerprint(); registerData.value = param; + registerData.value.fingerprint = fp.fingerprint; //校验密码是否一致 if (registerData.value.password !== registerData.value.repassword) { diff --git a/yarn.lock b/yarn.lock index 1101e13..c837c5f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -362,6 +362,13 @@ resolved "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz" integrity sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg== +"@fingerprintjs/fingerprintjs@^4.6.2": + version "4.6.2" + resolved "https://registry.npmjs.org/@fingerprintjs/fingerprintjs/-/fingerprintjs-4.6.2.tgz" + integrity sha512-g8mXuqcFKbgH2CZKwPfVtsUJDHyvcgIABQI7Y0tzWEFXpGxJaXuAuzlifT2oTakjDBLTK4Gaa9/5PERDhqUjtw== + dependencies: + tslib "^2.4.1" + "@floating-ui/core@^1.0.0": version "1.6.0" resolved "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz" @@ -3526,6 +3533,11 @@ ts-dedent@^2.2.0: resolved "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz" integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== +tslib@^2.4.1: + version "2.8.1" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + tslib@2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz"