From 4f8a44f6b535ce7a7270df1630a5efb48361749e Mon Sep 17 00:00:00 2001 From: zhang1106 <849185023@qq.com> Date: Tue, 7 Apr 2026 14:15:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=AD=98=E5=82=A8?= =?UTF-8?q?=E8=B0=83=E8=AF=95=E9=A1=B5=E9=9D=A2=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E8=A7=92=E8=89=B2=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/routes/auth.js | 24 +++--- frontend/package-lock.json | 56 ++++++------ frontend/src/context/AuthContext.jsx | 122 ++++++++++++--------------- frontend/src/utils/secureStorage.js | 40 ++++++--- 4 files changed, 124 insertions(+), 118 deletions(-) diff --git a/backend/routes/auth.js b/backend/routes/auth.js index b078967..a2d5b09 100644 --- a/backend/routes/auth.js +++ b/backend/routes/auth.js @@ -244,25 +244,23 @@ router.get('/profile', authMiddleware, async (req, res) => { }); } - const roles = await Role.findAll({ - include: [ - { - model: User, - where: { userId: req.user.userId }, - attributes: [], - }, - ], + const userRoles = await UserRole.findAll({ + where: { UserId: user.userId }, + include: [{ model: Role }], }); res.json({ success: true, data: { user, - roles: roles.map(r => ({ - roleId: r.roleId, - roleName: r.roleName, - roleCode: r.roleCode, - })), + roles: userRoles.map(ur => { + const role = ur.Role || ur; + return { + roleId: role.roleId, + roleName: role.roleName, + roleCode: role.roleCode, + }; + }), }, }); } catch (error) { diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 63defc3..b6411b8 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -278,6 +278,7 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -626,6 +627,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -669,6 +671,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -690,6 +693,7 @@ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", "license": "MIT", + "peer": true, "dependencies": { "@emotion/memoize": "^0.9.0" } @@ -1735,6 +1739,7 @@ "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.18.0.tgz", "integrity": "sha512-FYZZqD0UUHUswKz3LQl2Z7H24AhD14XGTsIRw3SJaXUxyfVMi+1yiZGmqTcPt/CkPpdU7rrxqcyQ1zJE5DjvIQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.17.8", "@types/react-reconciler": "^0.26.7", @@ -2507,8 +2512,7 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -2863,6 +2867,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz", "integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -2893,6 +2898,7 @@ "resolved": "https://registry.npmjs.org/@types/three/-/three-0.182.0.tgz", "integrity": "sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q==", "license": "MIT", + "peer": true, "dependencies": { "@dimforge/rapier3d-compat": "~0.12.0", "@tweenjs/tween.js": "~23.1.3", @@ -3038,7 +3044,6 @@ "integrity": "sha512-cuzPhD8fwRHk8IGfmYaR4eEe4cAyJEL66Ove/WZL7yWNL134nqLddSLwNRIsFlnnW1kK+p8Ck3viFnC0chXCXw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/parser": "^7.29.0", "@vue/shared": "3.5.29", @@ -3053,7 +3058,6 @@ "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=0.12" }, @@ -3066,8 +3070,7 @@ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@vue/compiler-dom": { "version": "3.5.29", @@ -3075,7 +3078,6 @@ "integrity": "sha512-n0G5o7R3uBVmVxjTIYcz7ovr8sy7QObFG8OQJ3xGCDNhbG60biP/P5KnyY8NLd81OuT1WJflG7N4KWYHaeeaIg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vue/compiler-core": "3.5.29", "@vue/shared": "3.5.29" @@ -3105,8 +3107,7 @@ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@vue/compiler-ssr": { "version": "3.5.29", @@ -3114,7 +3115,6 @@ "integrity": "sha512-Y/ARJZE6fpjzL5GH/phJmsFwx3g6t2KmHKHx5q+MLl2kencADKIrhH5MLF6HHpRMmlRAYBRSvv347Mepf1zVNw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.29", "@vue/shared": "3.5.29" @@ -3126,7 +3126,6 @@ "integrity": "sha512-zcrANcrRdcLtmGZETBxWqIkoQei8HaFpZWx/GHKxx79JZsiZ8j1du0VUJtu4eJjgFvU/iKL5lRXFXksVmI+5DA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vue/shared": "3.5.29" } @@ -3137,7 +3136,6 @@ "integrity": "sha512-8DpW2QfdwIWOLqtsNcds4s+QgwSaHSJY/SUe04LptianUQ/0xi6KVsu/pYVh+HO3NTVvVJjIPL2t6GdeKbS4Lg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vue/reactivity": "3.5.29", "@vue/shared": "3.5.29" @@ -3149,7 +3147,6 @@ "integrity": "sha512-AHvvJEtcY9tw/uk+s/YRLSlxxQnqnAkjqvK25ZiM4CllCZWzElRAoQnCM42m9AHRLNJ6oe2kC5DCgD4AUdlvXg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vue/reactivity": "3.5.29", "@vue/runtime-core": "3.5.29", @@ -3163,7 +3160,6 @@ "integrity": "sha512-G/1k6WK5MusLlbxSE2YTcqAAezS+VuwHhOvLx2KnQU7G2zCH6KIb+5Wyt6UjMq7a3qPzNEjJXs1hvAxDclQH+g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vue/compiler-ssr": "3.5.29", "@vue/shared": "3.5.29" @@ -3177,8 +3173,7 @@ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.29.tgz", "integrity": "sha512-w7SR0A5zyRByL9XUkCfdLs7t9XOHUyJ67qPGQjOou3p6GvBeBW+AVjUUmlxtZ4PIYaRvE+1LmK44O4uajlZwcg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webgpu/types": { "version": "0.1.69", @@ -3192,6 +3187,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3251,7 +3247,6 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -3651,6 +3646,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -4127,6 +4123,7 @@ "resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -4247,7 +4244,8 @@ "version": "1.11.19", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/debug": { "version": "4.4.3", @@ -4362,8 +4360,7 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/dom-helpers": { "version": "5.2.1", @@ -4662,6 +4659,7 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4722,6 +4720,7 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -6076,6 +6075,7 @@ "integrity": "sha512-GtldT42B8+jefDUC4yUKAvsaOrH7PDHmZxZXNgF2xMmymjUbRYJvpAybZAKEmXDGTM0mCsz8duOa4vTm5AY2Kg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@acemir/cssom": "^0.9.28", "@asamuzakjp/dom-selector": "^6.7.6", @@ -6284,7 +6284,6 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -6826,6 +6825,7 @@ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -6855,7 +6855,6 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -6871,7 +6870,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -6884,8 +6882,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/promise-worker-transferable": { "version": "1.0.4", @@ -7558,6 +7555,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -7582,6 +7580,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -8529,6 +8528,7 @@ "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -8555,7 +8555,8 @@ "version": "0.183.2", "resolved": "https://registry.npmjs.org/three/-/three-0.183.2.tgz", "integrity": "sha512-di3BsL2FEQ1PA7Hcvn4fyJOlxRRgFYBpMTcyOgkwJIaDOdJMebEFPA+t98EvjuljDx4hNulAGwF6KIjtwI5jgQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/three-mesh-bvh": { "version": "0.7.8", @@ -9134,6 +9135,7 @@ "integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.18.10", "postcss": "^8.4.27", @@ -9753,6 +9755,7 @@ "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -10158,6 +10161,7 @@ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/frontend/src/context/AuthContext.jsx b/frontend/src/context/AuthContext.jsx index 1a28290..2d73342 100644 --- a/frontend/src/context/AuthContext.jsx +++ b/frontend/src/context/AuthContext.jsx @@ -1,79 +1,66 @@ -import React, { createContext, useContext, useState, useEffect, useCallback } from 'react'; +import React, { createContext, useContext, useState, useEffect, useCallback, useMemo } from 'react'; import { authAPI } from '../api'; import secureStorage, { TOKEN_KEY, USER_KEY } from '../utils/secureStorage'; -const AuthContext = createContext(null); +const AuthContext = createContext({ + user: null, + token: null, + loading: true, + initialized: false, + login: async () => ({ success: false, message: '认证未初始化' }), + register: async () => ({ success: false, message: '认证未初始化' }), + logout: () => {}, + updateUser: () => {}, + hasPermission: () => false, + checkAdmin: () => Promise.resolve({ success: true, data: { hasAdmin: false, userCount: 0 } }), +}); export const useAuth = () => { - const context = useContext(AuthContext); - if (!context) { - throw new Error('useAuth必须在AuthProvider内部使用'); - } - return context; + return useContext(AuthContext); }; export const AuthProvider = ({ children }) => { - console.log('[AuthContext] Initializing...'); - const savedToken = secureStorage.get(TOKEN_KEY); const savedUser = secureStorage.get(USER_KEY); - console.log('[AuthContext] Saved token:', savedToken ? 'exists' : 'null'); - console.log('[AuthContext] Saved user:', savedUser ? 'exists' : 'null'); - - const [user, setUser] = useState(() => savedUser || null); - const [token, setToken] = useState(() => savedToken); - const [loading, setLoading] = useState(false); + const [user, setUser] = useState(savedUser); + const [token, setToken] = useState(savedToken); + const [loading, setLoading] = useState(true); const [initialized, setInitialized] = useState(false); useEffect(() => { const currentToken = secureStorage.get(TOKEN_KEY); - const currentUser = secureStorage.get(USER_KEY); - if (currentToken && currentToken === token) { - if (currentUser && JSON.stringify(currentUser) !== JSON.stringify(user)) { - setUser(currentUser); - } - // 有 token 时调用 fetchProfile 验证有效性并刷新用户信息 - fetchProfile(); - } else if (!currentToken) { + if (!currentToken) { setToken(null); setUser(null); - setInitialized(true); - } else { - setToken(currentToken); - setInitialized(true); - } - }, []); - - const fetchProfile = useCallback(async () => { - const currentToken = secureStorage.get(TOKEN_KEY); - if (!currentToken) { setLoading(false); setInitialized(true); return; } - try { - const response = await authAPI.getProfile(); - if (response.success) { - setUser(response.data.user); - secureStorage.set(USER_KEY, response.data.user); + const initializeAuth = async () => { + try { + const response = await authAPI.getProfile(); + if (response.success) { + setUser(response.data.user); + secureStorage.set(USER_KEY, response.data.user); + } + } catch { + secureStorage.remove(TOKEN_KEY); + secureStorage.remove(USER_KEY); + setToken(null); + setUser(null); + } finally { + setLoading(false); + setInitialized(true); } - } catch (error) { - console.error('获取用户信息失败:', error); - // Token 无效,清除登录状态 - secureStorage.remove(TOKEN_KEY); - secureStorage.remove(USER_KEY); - setToken(null); - setUser(null); - } finally { - setLoading(false); - setInitialized(true); - } + }; + + initializeAuth(); }, []); - const login = async (username, password) => { + const login = useCallback(async (username, password) => { try { const response = await authAPI.login({ username, password }); if (response.success) { @@ -89,9 +76,9 @@ export const AuthProvider = ({ children }) => { const message = error?.response?.data?.message || error?.message || '登录失败,请稍后重试'; return { success: false, message }; } - }; + }, []); - const register = async userData => { + const register = useCallback(async userData => { try { const response = await authAPI.register(userData); if (response.success) { @@ -109,7 +96,7 @@ export const AuthProvider = ({ children }) => { const message = error?.response?.data?.message || error?.message || '注册失败,请稍后重试'; return { success: false, message }; } - }; + }, []); const logout = useCallback(() => { secureStorage.remove(TOKEN_KEY); @@ -118,24 +105,25 @@ export const AuthProvider = ({ children }) => { setUser(null); }, []); - const updateUser = newUserData => { - const updatedUser = { ...user, ...newUserData }; - setUser(updatedUser); - secureStorage.set(USER_KEY, updatedUser); - }; + const updateUser = useCallback(newUserData => { + setUser(prev => { + const updated = { ...prev, ...newUserData }; + secureStorage.set(USER_KEY, updated); + return updated; + }); + }, []); - const hasPermission = permission => { + const hasPermission = useCallback(permission => { if (!user) return false; const roles = user.roles || []; - // 管理员拥有所有权限 if (roles.some(r => r.roleCode === 'admin')) return true; - // 检查具体权限 if (permission === 'admin') return roles.some(r => r.roleCode === 'admin'); - // roleCode 格式的权限直接匹配 return roles.some(r => r.roleCode === permission); - }; + }, [user]); - const value = { + const checkAdmin = useCallback(() => authAPI.checkAdmin(), []); + + const value = useMemo(() => ({ user, token, loading, @@ -145,10 +133,10 @@ export const AuthProvider = ({ children }) => { logout, updateUser, hasPermission, - checkAdmin: () => authAPI.checkAdmin(), - }; + checkAdmin, + }), [user, token, loading, initialized, login, register, logout, updateUser, hasPermission, checkAdmin]); return {children}; }; -export default AuthContext; +export default AuthContext; \ No newline at end of file diff --git a/frontend/src/utils/secureStorage.js b/frontend/src/utils/secureStorage.js index 6584ecb..6045d32 100644 --- a/frontend/src/utils/secureStorage.js +++ b/frontend/src/utils/secureStorage.js @@ -1,5 +1,4 @@ const STORAGE_PREFIX = 'idc_'; -const ENCRYPTION_ENABLED = import.meta.env.PROD; /** * 生成运行时密钥(基于浏览器指纹) @@ -11,7 +10,6 @@ const getOrCreateRuntimeKey = () => { let key = sessionStorage.getItem(KEY_STORAGE_ID); if (key) return key; - // 基于浏览器特征生成指纹作为密钥种子 const fingerprint = [ navigator.userAgent, screen.width, @@ -21,14 +19,12 @@ const getOrCreateRuntimeKey = () => { navigator.language, ].join('|'); - // 使用简单的哈希混合生成 32 字符密钥 let hash = 0; for (let i = 0; i < fingerprint.length; i++) { const char = fingerprint.charCodeAt(i); hash = ((hash << 5) - hash + char) | 0; } - // 生成 32 字符的随机密钥 const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; key = ''; let seed = Math.abs(hash) + Date.now(); @@ -38,12 +34,11 @@ const getOrCreateRuntimeKey = () => { } sessionStorage.setItem(KEY_STORAGE_ID, key); + console.log('[SecureStorage] New runtime key generated:', key.substring(0, 8) + '...'); return key; }; const simpleEncrypt = (text) => { - if (!ENCRYPTION_ENABLED) return text; - const key = getOrCreateRuntimeKey(); let result = ''; for (let i = 0; i < text.length; i++) { @@ -53,8 +48,6 @@ const simpleEncrypt = (text) => { }; const simpleDecrypt = (encrypted) => { - if (!ENCRYPTION_ENABLED) return encrypted; - try { const key = getOrCreateRuntimeKey(); const decoded = decodeURIComponent(atob(encrypted)); @@ -63,7 +56,8 @@ const simpleDecrypt = (encrypted) => { result += String.fromCharCode(decoded.charCodeAt(i) ^ key.charCodeAt(i % key.length)); } return result; - } catch { + } catch (e) { + console.error('[SecureStorage] Decrypt error:', e.message); return null; } }; @@ -80,6 +74,8 @@ export const secureStorage = { const serialized = JSON.stringify(data); const encrypted = simpleEncrypt(serialized); localStorage.setItem(storageKey, encrypted); + console.log('[SecureStorage] Set:', key, '=', value ? value.substring(0, 30) + '...' : 'null'); + console.log('[SecureStorage] Stored encrypted value length:', encrypted.length); return true; } catch (error) { console.error('[SecureStorage] Set error:', error); @@ -92,10 +88,25 @@ export const secureStorage = { const storageKey = `${STORAGE_PREFIX}${key}`; const encrypted = localStorage.getItem(storageKey); - if (!encrypted) return null; + if (!encrypted) { + console.log('[SecureStorage] Get:', key, '- not found in localStorage'); + return null; + } - const decrypted = simpleDecrypt(encrypted); + console.log('[SecureStorage] Get:', key, '- found encrypted data, length:', encrypted.length); + + let decrypted = simpleDecrypt(encrypted); if (!decrypted) { + console.log('[SecureStorage] Get:', key, '- decryption failed, trying raw JSON parse'); + try { + const parsed = JSON.parse(encrypted); + if (parsed && parsed.value !== undefined) { + console.log('[SecureStorage] Get:', key, '- recovered from raw JSON'); + return parsed.value; + } + } catch { + console.log('[SecureStorage] Get:', key, '- raw JSON parse also failed'); + } localStorage.removeItem(storageKey); return null; } @@ -103,10 +114,13 @@ export const secureStorage = { const data = JSON.parse(decrypted); if (data.expiry && Date.now() > data.expiry) { + console.log('[SecureStorage] Get:', key, '- expired'); localStorage.removeItem(storageKey); return null; } + const valuePreview = typeof data.value === 'string' ? data.value.substring(0, 30) + '...' : typeof data.value; + console.log('[SecureStorage] Get:', key, '- success:', valuePreview); return data.value; } catch (error) { console.error('[SecureStorage] Get error:', error); @@ -118,6 +132,7 @@ export const secureStorage = { try { const storageKey = `${STORAGE_PREFIX}${key}`; localStorage.removeItem(storageKey); + console.log('[SecureStorage] Remove:', key); return true; } catch (error) { console.error('[SecureStorage] Remove error:', error); @@ -135,6 +150,7 @@ export const secureStorage = { } } keys.forEach((key) => localStorage.removeItem(key)); + console.log('[SecureStorage] Clear: removed', keys.length, 'keys'); return true; } catch (error) { console.error('[SecureStorage] Clear error:', error); @@ -148,4 +164,4 @@ export const USER_KEY = 'user'; export const USER_ID_KEY = 'userId'; export const TICKET_COLUMNS_KEY = 'ticketVisibleColumns'; -export default secureStorage; +export default secureStorage; \ No newline at end of file