From 5a05e87f49914ce8457f536f2ca8aeb5a3fc9851 Mon Sep 17 00:00:00 2001 From: landaiqing <3517283258@qq.com> Date: Tue, 19 Nov 2024 01:48:32 +0800 Subject: [PATCH] :sparkles: add api signature --- .env.development | 2 + .env.production | 2 + package.json | 1 + src/api/{oauth => client}/index.ts | 0 src/api/user/index.ts | 31 +++------ .../src/CommentList/CommentList.vue | 4 +- src/directives/v-lazy-load.ts | 2 +- src/utils/alova/service.ts | 28 +++----- src/utils/axios/request.ts | 64 ------------------ src/utils/axios/web.ts | 10 --- src/utils/errorCode/errorCodeHandler.ts | 2 +- src/utils/signature/signature.ts | 65 ++++++++++++------- src/views/Login/LoginFooter.vue | 2 +- src/views/QRLogin/QRLogin.vue | 7 +- src/views/QRLogin/QRLoginFooter.vue | 2 +- src/vite-env.d.ts | 1 + yarn.lock | 5 ++ 17 files changed, 81 insertions(+), 147 deletions(-) rename src/api/{oauth => client}/index.ts (100%) delete mode 100644 src/utils/axios/request.ts delete mode 100644 src/utils/axios/web.ts diff --git a/.env.development b/.env.development index 70cba19..83fd53e 100644 --- a/.env.development +++ b/.env.development @@ -19,3 +19,5 @@ VITE_APP_TOKEN_KEY='Bearer' VITE_QR_SOCKET_URL='ws://127.0.0.1:80/api/ws/qrcode' VITE_MESSAGE_SOCKET_URL='ws://127.0.0.1:80/api/ws/message' + +VITE_FINGERPRINT_KEY='idm0jdoau38lwourb4pbjk4dxkat0kcx' diff --git a/.env.production b/.env.production index 43a9c48..d04ff66 100644 --- a/.env.production +++ b/.env.production @@ -18,3 +18,5 @@ VITE_APP_TOKEN_KEY='Bearer' VITE_QR_SOCKET_URL='wss://landaiqing.cn/api/ws/qr_ws' VITE_MESSAGE_SOCKET_URL='wss://landaiqing.cn/api/ws/message_ws' +# 签名密钥 +VITE_FINGERPRINT_KEY='idm0jdoau38lwourb4pbjk4dxkat0kcx' diff --git a/package.json b/package.json index 67fbd69..dd3bf5b 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "echarts": "^5.5.1", "eslint": "9.13.0", "go-captcha-vue": "^2", + "jsencrypt": "^3.3.2", "json-stringify-safe": "^5.0.1", "less": "^4.2.0", "localforage": "^1.10.0", diff --git a/src/api/oauth/index.ts b/src/api/client/index.ts similarity index 100% rename from src/api/oauth/index.ts rename to src/api/client/index.ts diff --git a/src/api/user/index.ts b/src/api/user/index.ts index 0ef90c4..a6145f5 100644 --- a/src/api/user/index.ts +++ b/src/api/user/index.ts @@ -1,32 +1,15 @@ import {service} from "@/utils/alova/service.ts"; import {AccountLogin, PhoneLogin, ResetPassword} from "@/types/user"; -/** - * 获取用户信息 - */ -export const getUserInfo = () => { - return service.Get('/api/auth/user/list', { - meta: { - ignoreToken: false - }, - cacheFor: { - // 设置缓存模式为持久化模式 - mode: 'restore', - // 缓存时间 - expire: 1000 * 10, - tag: 'v1' - } - }); - -}; /** * 刷新token */ export const refreshToken = () => { - return service.Post('/api/token/refresh', {}, { + return service.Post('/api/auth/token/refresh', {}, { meta: { authRole: 'refreshToken', - ignoreToken: false + ignoreToken: false, + signature: true } }); }; @@ -60,7 +43,8 @@ export const phoneLoginApi = (param: PhoneLogin) => { { meta: { ignoreToken: true, - authRole: 'login' + authRole: 'login', + signature: true } } ); @@ -80,7 +64,8 @@ export const accountLoginApi = (param: AccountLogin) => { { meta: { ignoreToken: true, - authRole: 'login' + authRole: 'login', + signature: true } } ); @@ -99,6 +84,7 @@ export const resetPasswordApi = (param: ResetPassword) => { { meta: { ignoreToken: true, + signature: true } } ); @@ -114,6 +100,7 @@ export const getUserDevice = () => { params: {}, meta: { ignoreToken: true, + signature: true } } ); diff --git a/src/components/CommentReply/src/CommentList/CommentList.vue b/src/components/CommentReply/src/CommentList/CommentList.vue index 06e045f..9753f81 100644 --- a/src/components/CommentReply/src/CommentList/CommentList.vue +++ b/src/components/CommentReply/src/CommentList/CommentList.vue @@ -292,7 +292,7 @@ async function cancelCommentLike(item: any) { * @param pageSize */ async function paginationCommentChange(page: number, pageSize: number) { - await router.push({ + router.push({ path: "/main", query: { type: router.currentRoute.value.query.type, @@ -331,7 +331,7 @@ async function getLatestCommentList() { query: { type: "latest", page: router.currentRoute.value.query.page, - } + } }); comment.commentLoading = false; }); diff --git a/src/directives/v-lazy-load.ts b/src/directives/v-lazy-load.ts index 4c84b8a..0cadacc 100644 --- a/src/directives/v-lazy-load.ts +++ b/src/directives/v-lazy-load.ts @@ -1,4 +1,4 @@ -const defaultImg: string = 'data:image/png;base64,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' +const defaultImg: string = 'data:image/png;base64,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'; import {useIntersectionObserver} from '@vueuse/core'; diff --git a/src/utils/alova/service.ts b/src/utils/alova/service.ts index 4b1bd75..29d2fe1 100644 --- a/src/utils/alova/service.ts +++ b/src/utils/alova/service.ts @@ -5,14 +5,14 @@ import useStore from "@/store"; import {localforageStorageAdapter} from "@/utils/alova/adapter/localforageStorageAdapter.ts"; import {createServerTokenAuthentication} from "alova/client"; import {AxiosError, AxiosResponse} from "axios"; -import {handleCode} from "@/utils/errorCode/errorCodeHandler.ts"; import {message, Modal} from "ant-design-vue"; import i18n from "@/locales"; import {axiosRequestAdapter} from "@alova/adapter-axios"; import {refreshToken} from "@/api/user"; -import createMD5Signature, {generateNonce} from "@/utils/signature/signature.ts"; +import generateKeySecretSignature from "@/utils/signature/signature.ts"; +import {handleErrorCode} from "@/utils/errorCode/errorCodeHandler.ts"; + -let hasShownNetworkError: boolean = false; const {onAuthRequired, onResponseRefreshToken} = createServerTokenAuthentication({ refreshTokenOnSuccess: { @@ -34,7 +34,7 @@ const {onAuthRequired, onResponseRefreshToken} = createServerTokenAuthentication } }); export const service = createAlova({ - timeout: 5000, + timeout: 10000, baseURL: import.meta.env.VITE_APP_BASE_API, statesHook: VueHook, // 请求适配器 @@ -50,15 +50,9 @@ export const service = createAlova({ } const lang = useStore().lang; method.config.headers['Accept-Language'] = lang.lang || 'zh'; - // 添加签名 - if (method.type === 'POST') { - const nonce: string = generateNonce(); // 生成随机的 Nonce - const {signature, timestamp}: { signature: string, timestamp: number } = createMD5Signature(method, nonce); - method.config.headers['X-Sign'] = signature; - method.config.headers['X-Timestamp'] = timestamp; - method.config.headers['X-Nonce'] = nonce; + if (method.meta?.signature) { + method.config.headers['X-Content-Security'] = generateKeySecretSignature(0, method.type, method.url, method.config.params, method.data); } - }), // 响应拦截器 responded: onResponseRefreshToken({ @@ -77,11 +71,6 @@ export const service = createAlova({ window.location.href = '/login'; }, 1000); }, - // onCancel() { - // setTimeout(() => { - // window.location.href = '/login'; - // },2000); - // } }); return Promise.reject(response.data); } @@ -92,9 +81,8 @@ export const service = createAlova({ onError: (error: AxiosError, _method: any) => { const {response} = error; - if (response && !hasShownNetworkError) { - hasShownNetworkError = true; - handleCode(response.status); + if (response) { + handleErrorCode(response.status); } if (!window.navigator.onLine) { message.error(i18n.global.t('error.networkError')).then(); diff --git a/src/utils/axios/request.ts b/src/utils/axios/request.ts deleted file mode 100644 index 4092258..0000000 --- a/src/utils/axios/request.ts +++ /dev/null @@ -1,64 +0,0 @@ -/** @format */ - -import axios, {AxiosInstance, AxiosRequestConfig} from "axios"; -import {message} from "ant-design-vue"; -import useStore from "@/store"; -import {handleCode} from "@/utils/errorCode/errorCodeHandler.ts"; - -class Request { - private instance: AxiosInstance | undefined; - - constructor(config: AxiosRequestConfig) { - this.instance = axios.create(config); - // 全局请求拦截 - this.instance.interceptors.request.use( - (config) => { - const user = useStore().user; - const token: string | undefined = user.user.accessToken; - if (token) { - config.headers.Authorization = `${import.meta.env.VITE_APP_TOKEN_KEY} ${token}`; - } - return config; - }, - (error) => { - return Promise.reject(error); - }, - ); - - // 全局响应拦截 - this.instance.interceptors.response.use( - (response) => { - if (response.data instanceof Blob) { - return response; - } else { - return response.data; - } - }, - (error) => { - const {response} = error; - if (response) { - handleCode(response.status); - } - if (!window.navigator.onLine) { - message.error("网络连接失败"); - return Promise.reject(error); - } - }, - ); - } - - request(config: AxiosRequestConfig): Promise { - return new Promise((resolve, reject) => { - this.instance - ?.request(config) - .then((res) => { - resolve(res); - }) - .catch((err) => { - reject(err); - }); - }); - } -} - -export default Request; diff --git a/src/utils/axios/web.ts b/src/utils/axios/web.ts deleted file mode 100644 index 3b73bd0..0000000 --- a/src/utils/axios/web.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** @format */ - -import Request from "./request"; - -const web: Request = new Request({ - baseURL: import.meta.env.VITE_APP_BASE_API, - timeout: 5000, -}); - -export default web; diff --git a/src/utils/errorCode/errorCodeHandler.ts b/src/utils/errorCode/errorCodeHandler.ts index a41c601..4c37c6a 100644 --- a/src/utils/errorCode/errorCodeHandler.ts +++ b/src/utils/errorCode/errorCodeHandler.ts @@ -1,6 +1,6 @@ import {message} from "ant-design-vue"; import i18n from "@/locales"; -export function handleCode(code: number): void { +export function handleErrorCode(code: number): void { switch (code) { case 400: message diff --git a/src/utils/signature/signature.ts b/src/utils/signature/signature.ts index 07a6867..1ad1053 100644 --- a/src/utils/signature/signature.ts +++ b/src/utils/signature/signature.ts @@ -1,33 +1,52 @@ import CryptoJS from 'crypto-js'; +import JSEncrypt from 'jsencrypt'; + +const rsaPublicKey = "-----BEGIN PUBLIC KEY-----" + + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCFe70Zi3OF7NuFi2saenJPjADW" + + "Ln402d142LOLBeN6cuWpItE3qgFsaMSorQApSM0recmAHMg4M4ly7+NgFPsaTzte" + + "MrO/LFCagwLWyyFJeqV4oQWRNQcFcGev8sTkUbIhhKpNAcmg37q8cmfI2eumycfl" + + "2FXuSyoJOa7hJgYNNQIDAQAB" + + "-----END PUBLIC KEY-----"; /** - * 生成 MD5 签名 - * @param method - * @param nonce + * 生成前端密钥 + * @param {string} type 请求类型(0或1) + * @param {string} method 请求方法(大写) + * @param {string} reqPath 请求路径(不包含host) + * @param {Object} reqQuery 请求参数 + * @param {string} reqBody 请求体 */ -export default function createMD5Signature(method: any, nonce: string) { - const secretKey: string = "38h0ex04du8qqf9ar2knn1quicdsm4s0"; // 密钥 - const timestamp: number = Date.now(); // 获取当前时间戳 - const payload: string = JSON.stringify(method.data || {}); // 获取请求数据 +export default function generateKeySecretSignature(type: number, method: string, reqPath: string, reqQuery: any, reqBody: any): string { + const time = (Date.now() / 1000).toFixed(); + const fingerprint = import.meta.env.VITE_FINGERPRINT_KEY as string; + // 生成 secret + const base64Key = btoa(fingerprint); + const secret = `type=${type};key=${base64Key};time=${time}`; - // 创建待签名字符串 - const baseString: string = `${method.type}:${payload}:${timestamp}:${nonce}:${secretKey}`; + const encryptor = new JSEncrypt(); + encryptor.setPublicKey(rsaPublicKey); + const encryptedSecret = encryptor.encrypt(secret); - // 生成 MD5 签名 - const signature: string = CryptoJS.MD5(baseString).toString(); + // 生成 signature + const sha256Hash = CryptoJS.SHA256(typeof reqBody === 'object' ? JSON.stringify(reqBody) : reqBody).toString(); - // 你可以根据需要返回包含时间戳的签名对象 - return { - signature, - timestamp, - nonce - }; + const signContent = [ + time, + method, + reqPath, + isEmptyObject(reqQuery) ? '' : JSON.stringify(reqQuery), + sha256Hash + ].join('\n'); + + const hmacHash = CryptoJS.HmacSHA256(signContent, fingerprint); + const signature = CryptoJS.enc.Base64.stringify(hmacHash); + + return `key=${fingerprint};secret=${encryptedSecret};signature=${signature}`; } - /** - * 生成随机字符串作为 nonce + * 判断对象是否为空 + * @param obj */ -export function generateNonce() { - return Math.random().toString(36).substring(2, 16); // 生成16位随机字符串 -} - +const isEmptyObject = (obj: any): boolean => { + return obj && Object.keys(obj).length === 0 && obj.constructor === Object; +}; diff --git a/src/views/Login/LoginFooter.vue b/src/views/Login/LoginFooter.vue index 85f9012..25a84b1 100644 --- a/src/views/Login/LoginFooter.vue +++ b/src/views/Login/LoginFooter.vue @@ -29,7 +29,7 @@ import {message} from "ant-design-vue"; import gitee from "@/assets/svgs/gitee.svg"; import {getQQUrl} from "@/api/oauth/qq.ts"; import {useDebounceFn} from "@vueuse/core"; -import {generateClientId} from "@/api/oauth"; +import {generateClientId} from "@/api/client"; import {getUserDevice} from "@/api/user"; const router = useRouter(); diff --git a/src/views/QRLogin/QRLogin.vue b/src/views/QRLogin/QRLogin.vue index 7f75226..520705a 100644 --- a/src/views/QRLogin/QRLogin.vue +++ b/src/views/QRLogin/QRLogin.vue @@ -56,12 +56,12 @@ import BoxDog from "@/components/BoxDog/BoxDog.vue"; import QRLoginFooter from "@/views/QRLogin/QRLoginFooter.vue"; import {useRouter} from 'vue-router'; import {generateQrCode} from "@/api/oauth/wechat.ts"; -import {onMounted, ref} from "vue"; +import {onBeforeUnmount, onMounted, ref} from "vue"; import logo from "@/assets/svgs/logo-schisandra.svg"; import useStore from "@/store"; import {message} from "ant-design-vue"; -import {generateClientId} from "@/api/oauth"; +import {generateClientId} from "@/api/client"; import {getUserDevice} from "@/api/user"; const {t} = useI18n(); @@ -133,6 +133,9 @@ onMounted(async () => { await getQrCode(); }); }); +onBeforeUnmount(() => { + websocket.close(false); +});