From 761ae74b0a2e2dcc7cbe8477338b08d3b4a4fa71 Mon Sep 17 00:00:00 2001 From: archer <545436317@qq.com> Date: Sat, 5 Aug 2023 11:32:43 +0800 Subject: [PATCH] perf: token --- client/src/api/fetch.ts | 4 +- client/src/api/request.ts | 6 +-- client/src/api/response/user.d.ts | 1 + client/src/pages/account/index.tsx | 4 +- .../pages/api/user/account/loginByPassword.ts | 8 ++-- client/src/pages/api/user/account/register.ts | 8 ++-- .../api/user/account/updatePasswordByCode.ts | 8 ++-- .../api/user/account/updatePasswordByOld.ts | 3 -- client/src/pages/login/index.tsx | 4 +- client/src/service/utils/auth.ts | 37 +++++++------------ client/src/service/utils/tools.ts | 17 ++++++++- client/src/utils/app.ts | 3 ++ client/src/utils/user.ts | 11 +++++- 13 files changed, 69 insertions(+), 45 deletions(-) diff --git a/client/src/api/fetch.ts b/client/src/api/fetch.ts index bce777db5..08b70d0e0 100644 --- a/client/src/api/fetch.ts +++ b/client/src/api/fetch.ts @@ -3,6 +3,7 @@ import { getErrText } from '@/utils/tools'; import { parseStreamChunk, SSEParseData } from '@/utils/sse'; import type { ChatHistoryItemResType } from '@/types/chat'; import { StartChatFnProps } from '@/components/ChatBox'; +import { getToken } from '@/utils/user'; interface StreamFetchProps { url?: string; @@ -24,7 +25,8 @@ export const streamFetch = ({ const response = await window.fetch(url, { method: 'POST', headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', + token: getToken() }, signal: abortSignal.signal, body: JSON.stringify({ diff --git a/client/src/api/request.ts b/client/src/api/request.ts index 6ce4c0594..fd6bbb6a8 100644 --- a/client/src/api/request.ts +++ b/client/src/api/request.ts @@ -1,5 +1,5 @@ import axios, { Method, InternalAxiosRequestConfig, AxiosResponse } from 'axios'; -import { clearCookie } from '@/utils/user'; +import { clearToken, getToken } from '@/utils/user'; import { TOKEN_ERROR_CODE } from '@/service/errorCode'; interface ConfigType { @@ -18,7 +18,7 @@ interface ResponseDataType { */ function requestStart(config: InternalAxiosRequestConfig): InternalAxiosRequestConfig { if (config.headers) { - // config.headers.Authorization = getToken(); + config.headers.token = getToken(); } return config; @@ -57,7 +57,7 @@ function responseError(err: any) { } // 有报错响应 if (err?.code in TOKEN_ERROR_CODE) { - clearCookie(); + clearToken(); window.location.replace( `/login?lastRoute=${encodeURIComponent(location.pathname + location.search)}` ); diff --git a/client/src/api/response/user.d.ts b/client/src/api/response/user.d.ts index c135fbb5a..f4b2077bd 100644 --- a/client/src/api/response/user.d.ts +++ b/client/src/api/response/user.d.ts @@ -2,6 +2,7 @@ import type { UserType } from '@/types/user'; import type { PromotionRecordSchema } from '@/types/mongoSchema'; export interface ResLogin { user: UserType; + token: string; } export interface PromotionRecordType { diff --git a/client/src/pages/account/index.tsx b/client/src/pages/account/index.tsx index 106440e47..4946119f5 100644 --- a/client/src/pages/account/index.tsx +++ b/client/src/pages/account/index.tsx @@ -3,7 +3,7 @@ import { Box, Flex, useTheme } from '@chakra-ui/react'; import { useGlobalStore } from '@/store/global'; import { useRouter } from 'next/router'; import dynamic from 'next/dynamic'; -import { clearCookie } from '@/utils/user'; +import { clearToken } from '@/utils/user'; import { useUserStore } from '@/store/user'; import { useConfirm } from '@/hooks/useConfirm'; import PageContainer from '@/components/PageContainer'; @@ -77,7 +77,7 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => { (tab: string) => { if (tab === TabEnum.loginout) { openConfirm(() => { - clearCookie(); + clearToken(); setUserInfo(null); router.replace('/login'); })(); diff --git a/client/src/pages/api/user/account/loginByPassword.ts b/client/src/pages/api/user/account/loginByPassword.ts index 831a96fca..2d6367304 100644 --- a/client/src/pages/api/user/account/loginByPassword.ts +++ b/client/src/pages/api/user/account/loginByPassword.ts @@ -3,7 +3,7 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@/service/response'; import { connectToDatabase } from '@/service/mongo'; import { User } from '@/service/models/user'; -import { setCookie } from '@/service/utils/tools'; +import { generateToken, setCookie } from '@/service/utils/tools'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -32,11 +32,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) throw new Error('密码错误'); } - setCookie(res, user._id); + const token = generateToken(user._id); + setCookie(res, token); jsonRes(res, { data: { - user + user, + token } }); } catch (err) { diff --git a/client/src/pages/api/user/account/register.ts b/client/src/pages/api/user/account/register.ts index 0292dd769..9e44936bb 100644 --- a/client/src/pages/api/user/account/register.ts +++ b/client/src/pages/api/user/account/register.ts @@ -4,7 +4,7 @@ import { jsonRes } from '@/service/response'; import { User } from '@/service/models/user'; import { AuthCode } from '@/service/models/authCode'; import { connectToDatabase } from '@/service/mongo'; -import { setCookie } from '@/service/utils/tools'; +import { generateToken, setCookie } from '@/service/utils/tools'; import { UserAuthTypeEnum } from '@/constants/common'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -56,11 +56,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< username }); - setCookie(res, user._id); + const token = generateToken(user._id); + setCookie(res, token); jsonRes(res, { data: { - user + user, + token } }); } catch (err) { diff --git a/client/src/pages/api/user/account/updatePasswordByCode.ts b/client/src/pages/api/user/account/updatePasswordByCode.ts index 736b6c06d..e4f038509 100644 --- a/client/src/pages/api/user/account/updatePasswordByCode.ts +++ b/client/src/pages/api/user/account/updatePasswordByCode.ts @@ -5,7 +5,7 @@ import { User } from '@/service/models/user'; import { AuthCode } from '@/service/models/authCode'; import { connectToDatabase } from '@/service/mongo'; import { UserAuthTypeEnum } from '@/constants/common'; -import { setCookie } from '@/service/utils/tools'; +import { generateToken, setCookie } from '@/service/utils/tools'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -48,11 +48,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< throw new Error('获取用户信息异常'); } - setCookie(res, user._id); + const token = generateToken(user._id); + setCookie(res, token); jsonRes(res, { data: { - user + user, + token } }); } catch (err) { diff --git a/client/src/pages/api/user/account/updatePasswordByOld.ts b/client/src/pages/api/user/account/updatePasswordByOld.ts index cec9cfeb0..2eb50eb75 100644 --- a/client/src/pages/api/user/account/updatePasswordByOld.ts +++ b/client/src/pages/api/user/account/updatePasswordByOld.ts @@ -2,10 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@/service/response'; import { User } from '@/service/models/user'; -import { AuthCode } from '@/service/models/authCode'; import { connectToDatabase } from '@/service/mongo'; -import { UserAuthTypeEnum } from '@/constants/common'; -import { setCookie } from '@/service/utils/tools'; import { authUser } from '@/service/utils/auth'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { diff --git a/client/src/pages/login/index.tsx b/client/src/pages/login/index.tsx index 9011a4306..7e93bfecc 100644 --- a/client/src/pages/login/index.tsx +++ b/client/src/pages/login/index.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback, useEffect } from 'react'; +import React, { useState, useCallback } from 'react'; import styles from './index.module.scss'; import { Box, Flex, Image } from '@chakra-ui/react'; import { PageTypeEnum } from '@/constants/user'; @@ -10,6 +10,7 @@ import { useChatStore } from '@/store/chat'; import LoginForm from './components/LoginForm'; import dynamic from 'next/dynamic'; import { serviceSideProps } from '@/utils/i18n'; +import { setToken } from '@/utils/user'; const RegisterForm = dynamic(() => import('./components/RegisterForm')); const ForgetPasswordForm = dynamic(() => import('./components/ForgetPasswordForm')); @@ -28,6 +29,7 @@ const Login = () => { setLastChatAppId(''); setUserInfo(res.user); + setToken(res.token); setTimeout(() => { router.push(lastRoute ? decodeURIComponent(lastRoute) : '/app/list'); }, 100); diff --git a/client/src/service/utils/auth.ts b/client/src/service/utils/auth.ts index 13aecb0dd..cc52110c8 100644 --- a/client/src/service/utils/auth.ts +++ b/client/src/service/utils/auth.ts @@ -1,9 +1,9 @@ import type { NextApiRequest } from 'next'; -import jwt from 'jsonwebtoken'; import Cookie from 'cookie'; import { App, OpenApi, User, OutLink, KB } from '../mongo'; import type { AppSchema } from '@/types/mongoSchema'; import { ERROR_ENUM } from '../errorCode'; +import { authJWT } from './tools'; export enum AuthUserTypeEnum { token = 'token', @@ -11,26 +11,16 @@ export enum AuthUserTypeEnum { apikey = 'apikey' } -export const parseCookie = (cookie?: string): Promise => { - return new Promise((resolve, reject) => { - // 获取 cookie - const cookies = Cookie.parse(cookie || ''); - const token = cookies.token; +export const authCookieToken = async (cookie?: string, token?: string): Promise => { + // 获取 cookie + const cookies = Cookie.parse(cookie || ''); + const cookieToken = cookies.token || token; - if (!token) { - return reject(ERROR_ENUM.unAuthorization); - } + if (!cookieToken) { + return Promise.reject(ERROR_ENUM.unAuthorization); + } - const key = process.env.TOKEN_KEY as string; - - jwt.verify(token, key, function (err, decoded: any) { - if (err || !decoded?.userId) { - reject(ERROR_ENUM.unAuthorization); - return; - } - resolve(decoded.userId); - }); - }); + return await authJWT(cookieToken); }; /* auth balance */ @@ -117,8 +107,9 @@ export const authUser = async ({ return userId; }; - const { cookie, apikey, rootkey, userid, authorization } = (req.headers || {}) as { + const { cookie, token, apikey, rootkey, userid, authorization } = (req.headers || {}) as { cookie?: string; + token?: string; apikey?: string; rootkey?: string; userid?: string; @@ -130,13 +121,13 @@ export const authUser = async ({ let authType: `${AuthUserTypeEnum}` = AuthUserTypeEnum.token; if (authToken) { - uid = await parseCookie(cookie); + uid = await authCookieToken(cookie, token); authType = AuthUserTypeEnum.token; } else if (authRoot) { uid = await parseRootKey(rootkey, userid); authType = AuthUserTypeEnum.root; - } else if (cookie) { - uid = await parseCookie(cookie); + } else if (cookie || token) { + uid = await authCookieToken(cookie, token); authType = AuthUserTypeEnum.token; } else if (apikey) { uid = await parseOpenApiKey(apikey); diff --git a/client/src/service/utils/tools.ts b/client/src/service/utils/tools.ts index 60daf8484..02fea88b6 100644 --- a/client/src/service/utils/tools.ts +++ b/client/src/service/utils/tools.ts @@ -4,6 +4,7 @@ import crypto from 'crypto'; import jwt from 'jsonwebtoken'; import { generateQA } from '../events/generateQA'; import { generateVector } from '../events/generateVector'; +import { ERROR_ENUM } from '../errorCode'; /* 密码加密 */ export const hashPassword = (psw: string) => { @@ -22,12 +23,24 @@ export const generateToken = (userId: string) => { ); return token; }; +// auth token +export const authJWT = (token: string) => + new Promise((resolve, reject) => { + const key = process.env.TOKEN_KEY as string; + jwt.verify(token, key, function (err, decoded: any) { + if (err || !decoded?.userId) { + reject(ERROR_ENUM.unAuthorization); + return; + } + resolve(decoded.userId); + }); + }); /* set cookie */ -export const setCookie = (res: NextApiResponse, userId: string) => { +export const setCookie = (res: NextApiResponse, token: string) => { res.setHeader( 'Set-Cookie', - `token=${generateToken(userId)}; Path=/; HttpOnly; Max-Age=604800; Samesite=None; Secure;` + `token=${token}; Path=/; HttpOnly; Max-Age=604800; Samesite=None; Secure;` ); }; /* clear cookie */ diff --git a/client/src/utils/app.ts b/client/src/utils/app.ts index a0c092cf4..c50a92309 100644 --- a/client/src/utils/app.ts +++ b/client/src/utils/app.ts @@ -331,6 +331,7 @@ const simpleChatTemplate = (formData: EditFormType): AppModuleItemType[] => [ name: 'AI 对话', flowType: FlowModuleTypeEnum.chatNode, inputs: chatModelInput(formData), + showStatus: true, outputs: [ { key: 'answerText', @@ -426,6 +427,7 @@ const kbTemplate = (formData: EditFormType): AppModuleItemType[] => [ { name: '知识库搜索', flowType: FlowModuleTypeEnum.kbSearchNode, + showStatus: true, inputs: [ { key: 'kbList', @@ -537,6 +539,7 @@ const kbTemplate = (formData: EditFormType): AppModuleItemType[] => [ name: 'AI 对话', flowType: FlowModuleTypeEnum.chatNode, inputs: chatModelInput(formData), + showStatus: true, outputs: [ { key: 'answerText', diff --git a/client/src/utils/user.ts b/client/src/utils/user.ts index e0a8667d3..38462583c 100644 --- a/client/src/utils/user.ts +++ b/client/src/utils/user.ts @@ -1,14 +1,23 @@ import { PRICE_SCALE } from '@/constants/common'; import { loginOut } from '@/api/user'; -export const clearCookie = () => { +const tokenKey = 'token'; +export const clearToken = () => { try { loginOut(); + localStorage.removeItem(tokenKey); } catch (error) { error; } }; +export const setToken = (token: string) => { + localStorage.setItem(tokenKey, token); +}; +export const getToken = () => { + return localStorage.getItem(tokenKey) || ''; +}; + /** * 把数据库读取到的price,转化成元 */