From 3b6d02e21e0c982a7c79f0e07d3f61319a81f213 Mon Sep 17 00:00:00 2001 From: zhang zhuo Date: Sat, 10 Jan 2026 16:15:47 +0800 Subject: [PATCH] =?UTF-8?q?=E7=99=BB=E5=BD=95=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 11 ++++ .env.production | 11 ++++ package.json | 2 + src/config/index.ts | 2 + src/router/index.ts | 6 +- src/utils/route.ts | 26 ++++++++ src/utils/tools.ts | 72 +++++++++++---------- src/views/dashboard/message/detail.vue | 86 -------------------------- src/views/system/login/index.vue | 8 +-- tsconfig.json | 32 ++-------- vite-env.d.ts | 1 + 11 files changed, 104 insertions(+), 153 deletions(-) delete mode 100644 src/views/dashboard/message/detail.vue diff --git a/.env.development b/.env.development index 4165644..4aa45e0 100644 --- a/.env.development +++ b/.env.development @@ -7,3 +7,14 @@ VITE_APP_ENV='development' # 开发环境 VITE_API_BASE='https://demo.leapy.cn' VITE_WS_URL='wss://demo.leapy.cn/ws' + +# SM2公钥 +VITE_RSA_PUBLIC_KEY='-----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgSGuTW9sIHkDCOcXe0Gk + euU82C9vGDaL569ChxdG3Ab4BCu1LqtGARMBeTW99amHbRbJE4/dzafUsGEh2rOq + /yb/qwKixtk1PWfrcqTuHJ8vmuj9MCQ0EIirKzc7lvGDUoIQuGAyQMQOTx2/iiDW + Kk50n1wtA4j8CITNuvZIXcfyKcPNtrgvBnhIuBVuZ7+X8oEjiO4nknVN2HrgeDK7 + aQ4B43MR9rraqaupOv2l8Ua1nwMI3BtBdhQgXkjzMruHehL5+Bq4EHY01mCccUWv + 7YoL2R9Idu8KKxvMxypO1SffMGj3ViE4TvAQHU+eRrnXWDv2c7WCXSzSZUPfXalO + EwIDAQAB + -----END PUBLIC KEY-----' diff --git a/.env.production b/.env.production index 70bd6a7..9509830 100644 --- a/.env.production +++ b/.env.production @@ -7,3 +7,14 @@ VITE_APP_ENV='production' # 生产环境 VITE_API_BASE='https://demo.leapy.cn' VITE_WS_URL='wss://demo.leapy.cn/ws' + +# SM2公钥 +VITE_RSA_PUBLIC_KEY='-----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgSGuTW9sIHkDCOcXe0Gk + euU82C9vGDaL569ChxdG3Ab4BCu1LqtGARMBeTW99amHbRbJE4/dzafUsGEh2rOq + /yb/qwKixtk1PWfrcqTuHJ8vmuj9MCQ0EIirKzc7lvGDUoIQuGAyQMQOTx2/iiDW + Kk50n1wtA4j8CITNuvZIXcfyKcPNtrgvBnhIuBVuZ7+X8oEjiO4nknVN2HrgeDK7 + aQ4B43MR9rraqaupOv2l8Ua1nwMI3BtBdhQgXkjzMruHehL5+Bq4EHY01mCccUWv + 7YoL2R9Idu8KKxvMxypO1SffMGj3ViE4TvAQHU+eRrnXWDv2c7WCXSzSZUPfXalO + EwIDAQAB + -----END PUBLIC KEY-----' diff --git a/package.json b/package.json index fb28a84..297e975 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "element-plus": "2.13.0", "highlight.js": "^11.11.1", "image-conversion": "^2.1.1", + "jsencrypt": "^3.5.4", "nprogress": "^0.2.0", "pinia": "^3.0.4", "pinia-plugin-persistedstate": "^4.7.1", @@ -37,6 +38,7 @@ "xgplayer-hls": "^3.0.23" }, "devDependencies": { + "@types/crypto-js": "^4.2.2", "@types/node": "^25.0.3", "@vitejs/plugin-vue": "^6.0.3", "eslint": "^9.39.2", diff --git a/src/config/index.ts b/src/config/index.ts index b680265..f69dbfe 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -22,4 +22,6 @@ export default { //布局 分栏:column | 通栏:header | 经典:menu | 功能坞:dock //dock将关闭标签和面包屑栏 APP_LAYOUT: 'column', + // sm2公钥 + RSA_PUBLIC_KEY: import.meta.env.VITE_RSA_PUBLIC_KEY } diff --git a/src/router/index.ts b/src/router/index.ts index abaf6e4..6d23741 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -7,7 +7,7 @@ import tools from '@/utils/tools' import api from "@/api" import sRouter from './system' import {treeFilter, filterAsyncRouter, flatAsyncRoutes} from '@/utils/route' -import {beforeEach, afterEach} from '@/utils/route' +import {beforeEach, afterEach, makeMenu} from '@/utils/route' import i18n from "@/locales" //系统路由 @@ -59,7 +59,7 @@ router.beforeEach(async (to, from, next) => { /* @ts-ignore */ const res = await api.auth.menu() /* @ts-ignore */ - tools.data.set("MENU", tools.makeMenu(res.data.menus, 0)) + tools.data.set("MENU", makeMenu(res.data.menus, 0)) /* @ts-ignore */ tools.data.set("PERMISSIONS", res.data.buttons) /* @ts-ignore */ @@ -72,7 +72,7 @@ router.beforeEach(async (to, from, next) => { }) let menu = [...userMenu, ...apiMenu] - var menuRouter = filterAsyncRouter(menu) + let menuRouter = filterAsyncRouter(menu); menuRouter = flatAsyncRoutes(menuRouter) menuRouter.forEach(item => { router.addRoute("layout", item) diff --git a/src/utils/route.ts b/src/utils/route.ts index ed5402a..371ba6a 100644 --- a/src/utils/route.ts +++ b/src/utils/route.ts @@ -112,3 +112,29 @@ export function getMenu() { }) return [...userMenu, ...apiMenu] } + +export function makeMenu(menus, pid = 0) { + const arr = []; + for (let item of menus) { + if (item.pid === pid) { + // 数据格式处理 + const tmp = { + name: item['name'], + path: item['type'] == 0 ? '/' + item['path'] : item['path'], + component: item['path'], + meta: { + 'title': item['title'], + 'icon': item['icon'], + 'hidden': item['hidden'], + 'type': item['type'] == 0 ? 'menu' : 'link' + } + }; + const children = makeMenu(menus, item.menu_id); + if (children.length > 0) { + tmp['children'] = children + } + arr.push(tmp) + } + } + return arr +} diff --git a/src/utils/tools.ts b/src/utils/tools.ts index 9fc034f..b036761 100644 --- a/src/utils/tools.ts +++ b/src/utils/tools.ts @@ -1,4 +1,6 @@ -import CryptoJS from 'crypto-js'; +import CryptoJS from 'crypto-js' +import JSEncrypt from 'jsencrypt' +import config from "@/config" const tools = { data: { @@ -11,7 +13,9 @@ const tools = { }, get(cacheKey: string) { try { - const cacheValue = JSON.parse(tools.base64.decrypt(localStorage.getItem(cacheKey))) + const data = localStorage.getItem(cacheKey); + if (!data) return null + const cacheValue = JSON.parse(tools.base64.decrypt(data)) if (cacheValue) { let nowTime = new Date().getTime() if (nowTime > cacheValue.expireIn && cacheValue.expireIn !== 0) { @@ -48,36 +52,36 @@ const tools = { return [null, err] } }, - crypto: { - //MD5加密 - MD5(data: string) { - return CryptoJS.MD5(data).toString() - }, + md5: function (data: string) { + return CryptoJS.MD5(data).toString() }, - makeMenu: function (menus, pid = 0) { - const arr = []; - for (let item of menus) { - if (item.pid === pid) { - // 数据格式处理 - const tmp = { - name: item['name'], - path: item['type'] == 0 ? '/' + item['path'] : item['path'], - component: item['path'], - meta: { - 'title': item['title'], - 'icon': item['icon'], - 'hidden': item['hidden'], - 'type': item['type'] == 0 ? 'menu' : 'link' - } - }; - const children = this.makeMenu(menus, item.menu_id); - if (children.length > 0) { - tmp['children'] = children - } - arr.push(tmp) - } + aes: { + encrypt(message: string, secretKey: string): string { + return CryptoJS.AES.encrypt(message, secretKey).toString() + }, + decrypt(ciphertext: string, secretKey: string) { + CryptoJS.AES.decrypt(ciphertext, secretKey).toString(CryptoJS.enc.Utf8) + } + }, + rsa: { + encrypt(message: string): string { + const encryptor = new JSEncrypt() + encryptor.setPublicKey(config.RSA_PUBLIC_KEY) + const encrypted = encryptor.encrypt(message) + if (!encrypted) { + throw new Error('RSA encrypt failed') + } + return encrypted; + }, + decrypt(cipherText: string, privateKey: string): string { + const decryptor = new JSEncrypt() + decryptor.setPrivateKey(privateKey) + const decrypted = decryptor.decrypt(cipherText) + if (!decrypted) { + throw new Error('RSA decrypt failed') + } + return decrypted } - return arr }, screen: function (element) { var isFull = !!(document.webkitIsFullScreen || document.mozFullScreen || document.msFullscreenElement || document.fullscreenElement); @@ -129,16 +133,13 @@ const tools = { } return fmt; }, - randomUUIDString: function (len = 16) { - return crypto.randomUUID().replace(/-/g, '').slice(0, len) - }, makeTreeData: function (data, pid = 0, key = "id", parent = "parent_id") { const arr = []; for (let item of data) { if (item[parent] == pid) { // 数据格式处理 const tmp = item; - const children = tools.makeTreeData(data, item[key], key, parent); + const children = this.makeTreeData(data, item[key], key, parent); if (children.length > 0) { tmp['children'] = children } @@ -147,6 +148,9 @@ const tools = { } return arr }, + randomUUIDString: function (len = 16) { + return crypto.randomUUID().replace(/-/g, '').slice(0, len) + }, getBrowser(userAgent: string) { // 检测浏览器类型和版本 if (/Opera|OPR/.test(userAgent)) { diff --git a/src/views/dashboard/message/detail.vue b/src/views/dashboard/message/detail.vue deleted file mode 100644 index 05e3252..0000000 --- a/src/views/dashboard/message/detail.vue +++ /dev/null @@ -1,86 +0,0 @@ - - - - - diff --git a/src/views/system/login/index.vue b/src/views/system/login/index.vue index 449c07a..53dc899 100644 --- a/src/views/system/login/index.vue +++ b/src/views/system/login/index.vue @@ -186,11 +186,11 @@ async function login() { } isLogin.value = true const data = { - username: form.value.username, - password: tools.crypto.MD5(form.value.password), + username: tools.rsa.encrypt(form.value.username), + password: tools.rsa.encrypt(tools.md5(form.value.password)), uuid: form.value.uuid, - code: form.value.code - }; + code: tools.rsa.encrypt(form.value.code) + } // 登录接口 const [res, err] = await tools.go(api.auth.login(data)) isLogin.value = false diff --git a/tsconfig.json b/tsconfig.json index 14bdc73..8a9d0fc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,33 +1,13 @@ { "compilerOptions": { - "target": "ESNext", - "useDefineForClassFields": true, "module": "ESNext", - "moduleResolution": "Bundler", - "lib": [ - "ESNext", - "DOM" - ], - "jsx": "preserve", - "strict": true, - "skipLibCheck": true, - "types": [ - "node", - "vite/client" - ], - "baseUrl": ".", + "moduleResolution": "node", + "types": ["node"], + "baseUrl": "./", "paths": { - "@/*": [ - "src/*" - ] + "@/*": ["src/*"] }, "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "ignoreDeprecations": "6.0" - }, - "include": [ - "src/**/*.ts", - "src/**/*.tsx", - "src/**/*.vue" - ] + "allowSyntheticDefaultImports": true + } } diff --git a/vite-env.d.ts b/vite-env.d.ts index ca9eb6c..7399b62 100644 --- a/vite-env.d.ts +++ b/vite-env.d.ts @@ -12,6 +12,7 @@ interface ImportMetaEnv { readonly VITE_APP_TITLE: string readonly VITE_WS_URL: string, readonly VITE_APP_ENV: string + readonly VITE_RSA_PUBLIC_KEY: string } interface Document {