From 34fcf4ca26dc9767feab8b55e0beac609a0b86b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A8=B1=E5=90=B9=E9=9B=AA?= Date: Fri, 20 Jan 2023 09:56:12 +0800 Subject: [PATCH] =?UTF-8?q?feat(projects):=20=E6=8A=BD=E7=A6=BB=E9=83=A8?= =?UTF-8?q?=E5=88=86=E7=99=BB=E9=99=86=E9=80=BB=E8=BE=91=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81Token=E6=88=96JWT=E6=A0=A1=E9=AA=8C=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mock/api/auth.ts | 4 +- src/service/api/auth.ts | 15 ++++- src/service/request/instance.ts | 4 +- src/store/modules/auth/auth-type.ts | 46 +++++++++++++++ src/store/modules/auth/index.ts | 90 ++++++++++++++--------------- src/typings/api.d.ts | 6 +- 6 files changed, 109 insertions(+), 56 deletions(-) create mode 100644 src/store/modules/auth/auth-type.ts diff --git a/mock/api/auth.ts b/mock/api/auth.ts index 8435b747..6f0af019 100644 --- a/mock/api/auth.ts +++ b/mock/api/auth.ts @@ -23,7 +23,7 @@ const apis: MockMethod[] = [ { url: '/mock/login', method: 'post', - response: (options: Service.MockOption): Service.MockServiceResult => { + response: (options: Service.MockOption): Service.MockServiceResult => { const { userName = undefined, password = undefined } = options.body; if (!userName || !password) { @@ -101,7 +101,7 @@ const apis: MockMethod[] = [ { url: '/mock/updateToken', method: 'post', - response: (options: Service.MockOption): Service.MockServiceResult => { + response: (options: Service.MockOption): Service.MockServiceResult => { const { refreshToken = '' } = options.body; const findItem = userModel.find(item => item.refreshToken === refreshToken); diff --git a/src/service/api/auth.ts b/src/service/api/auth.ts index f717362d..78f0c411 100644 --- a/src/service/api/auth.ts +++ b/src/service/api/auth.ts @@ -10,14 +10,23 @@ export function fetchSmsCode(phone: string) { } /** - * 登录 + * 登录(Token认证方式) * @param userName - 用户名 * @param password - 密码 */ -export function fetchLogin(userName: string, password: string) { +export function fetchLoginToken(userName: string, password: string) { return mockRequest.post('/login', { userName, password }); } +/** + * 登录(JWT认证方式) + * @param userName - 用户名 + * @param password - 密码 + */ +export function fetchLoginJWT(userName: string, password: string) { + return mockRequest.post('/login', { userName, password }); +} + /** 获取用户信息 */ export function fetchUserInfo() { return mockRequest.get('/getUserInfo'); @@ -37,5 +46,5 @@ export function fetchUserRoutes(userId: string) { * @param refreshToken */ export function fetchUpdateToken(refreshToken: string) { - return mockRequest.post('/updateToken', { refreshToken }); + return mockRequest.post('/updateToken', { refreshToken }); } diff --git a/src/service/request/instance.ts b/src/service/request/instance.ts index c4180c9f..1b98c4c5 100644 --- a/src/service/request/instance.ts +++ b/src/service/request/instance.ts @@ -9,7 +9,7 @@ import { handleServiceResult, transformRequestData } from '@/utils'; -import { handleRefreshToken } from './helpers'; +import { defaultAuthType } from '@/store/modules/auth/auth-type'; /** * 封装axios请求类 @@ -71,7 +71,7 @@ export default class CustomAxiosInstance { // token失效, 刷新token if (REFRESH_TOKEN_CODE.includes(backend[codeKey])) { - const config = await handleRefreshToken(response.config); + const config = await defaultAuthType.handleTokenFailure(response.config); if (config) { return this.instance.request(config); } diff --git a/src/store/modules/auth/auth-type.ts b/src/store/modules/auth/auth-type.ts new file mode 100644 index 00000000..d868ea8b --- /dev/null +++ b/src/store/modules/auth/auth-type.ts @@ -0,0 +1,46 @@ +import type { AxiosRequestConfig } from 'axios'; +import { fetchLoginToken, fetchLoginJWT } from '@/service'; +import { useAuthStore } from '@/store'; +import { localStg } from '@/utils'; +import { handleRefreshToken } from '@/service/request/helpers'; + +const TokenAuth = { + /** 获取token的接口 */ + fetchLogin: fetchLoginToken, + /** 请求登录成功后的数据处理回调 */ + handleAuthToken(backendToken: ApiAuth.Token) { + // 先把token存储到缓存中(后面接口的请求头需要token) + const { token } = backendToken; + // token由后端控制刷新,不需要前端处理刷新逻辑 + localStg.set('token', token, null); + }, + /** Token失效后的回调 */ + async handleTokenFailure(_: AxiosRequestConfig) { + // token失效说明已经超过了可刷新token时间,需要重新登录 + const { resetAuthStore } = useAuthStore(); + resetAuthStore(); + } +}; + +const JWTAuth = { + fetchLogin: fetchLoginJWT, + /** 请求登录成功后的数据处理回调 */ + handleAuthToken(backendToken: ApiAuth.JWT) { + // 先把token存储到缓存中(后面接口的请求头需要token) + const { token, refreshToken } = backendToken; + localStg.set('token', token); + localStg.set('refreshToken', refreshToken); + }, + /** Token失效后的回调 */ + async handleTokenFailure(axiosConfig: AxiosRequestConfig) { + return handleRefreshToken(axiosConfig); + } +}; + +/** + * 选择需要使用哪种认证方式,默认选择JWT方式 + */ +const defaultAuthType = JWTAuth; + +// 如果只使用JWTAuth,不导出会导致TokenAuth报未使用的ts、eslint错误 +export { defaultAuthType, JWTAuth, TokenAuth }; diff --git a/src/store/modules/auth/index.ts b/src/store/modules/auth/index.ts index 57f02368..d6d35552 100644 --- a/src/store/modules/auth/index.ts +++ b/src/store/modules/auth/index.ts @@ -1,11 +1,12 @@ import { unref, nextTick } from 'vue'; import { defineStore } from 'pinia'; import { router } from '@/router'; -import { fetchLogin, fetchUserInfo } from '@/service'; +import { fetchUserInfo } from '@/service'; +import { useRouteStore } from '@/store'; import { useRouterPush } from '@/composables'; import { localStg } from '@/utils'; import { useTabStore } from '../tab'; -import { useRouteStore } from '../route'; +import { defaultAuthType } from './auth-type'; import { getToken, getUserInfo, clearAuthStorage } from './helpers'; interface AuthState { @@ -53,59 +54,35 @@ export const useAuthStore = defineStore('auth-store', { * 处理登录后成功或失败的逻辑 * @param backendToken - 返回的token */ - async handleActionAfterLogin(backendToken: ApiAuth.Token) { + async handleActionAfterLogin(backendToken: ApiAuth.Token | ApiAuth.JWT) { const route = useRouteStore(); const { toLoginRedirect } = useRouterPush(false); - const loginSuccess = await this.loginByToken(backendToken); - - if (loginSuccess) { - await route.initAuthRoute(); - - // 跳转登录后的地址 - toLoginRedirect(); - - // 登录成功弹出欢迎提示 - if (route.isInitAuthRoute) { - window.$notification?.success({ - title: '登录成功!', - content: `欢迎回来,${this.userInfo.userName}!`, - duration: 3000 - }); - } + // 保存token到store + this.token = backendToken.token; + // 获取用户信息 + const userInfo = await this.updateUserInfo(); + if (!userInfo) { + // 获取数据失败,重置状态,退出 + this.resetAuthStore(); return; } - // 不成功则重置状态 - this.resetAuthStore(); - }, - /** - * 根据token进行登录 - * @param backendToken - 返回的token - */ - async loginByToken(backendToken: ApiAuth.Token) { - let successFlag = false; + // 初始化路由 + await route.initAuthRoute(); - // 先把token存储到缓存中(后面接口的请求头需要token) - const { token, refreshToken } = backendToken; - localStg.set('token', token); - localStg.set('refreshToken', refreshToken); + // 跳转登录后的地址 + toLoginRedirect(); - // 获取用户信息 - const { data } = await fetchUserInfo(); - if (data) { - // 成功后把用户信息存储到缓存中 - localStg.set('userInfo', data); - - // 更新状态 - this.userInfo = data; - this.token = token; - - successFlag = true; + // 登录成功弹出欢迎提示 + if (route.isInitAuthRoute) { + window.$notification?.success({ + title: '登录成功!', + content: `欢迎回来,${this.userInfo.userName}!`, + duration: 3000 + }); } - - return successFlag; }, /** * 登录 @@ -114,12 +91,28 @@ export const useAuthStore = defineStore('auth-store', { */ async login(userName: string, password: string) { this.loginLoading = true; - const { data } = await fetchLogin(userName, password); + const { data } = await defaultAuthType.fetchLogin(userName, password); if (data) { + // 登录成功后对返回的Token或者JWT处理 + defaultAuthType.handleAuthToken(data); await this.handleActionAfterLogin(data); } this.loginLoading = false; }, + /** + * 查询用户信息并更新到store + */ + async updateUserInfo() { + const { data } = await fetchUserInfo(); + if (data) { + // 成功后把用户信息存储到缓存中 + localStg.set('userInfo', data); + // 更新状态 + this.userInfo = data; + return data; + } + return null; + }, /** * 更换用户权限(切换账号) * @param userRole @@ -142,9 +135,10 @@ export const useAuthStore = defineStore('auth-store', { } }; const { userName, password } = accounts[userRole]; - const { data } = await fetchLogin(userName, password); + const { data } = await defaultAuthType.fetchLogin(userName, password); if (data) { - await this.loginByToken(data); + this.token = data.token; + defaultAuthType.handleAuthToken(data); resetRouteStore(); initAuthRoute(); } diff --git a/src/typings/api.d.ts b/src/typings/api.d.ts index 1ede6340..8ba18f10 100644 --- a/src/typings/api.d.ts +++ b/src/typings/api.d.ts @@ -2,9 +2,13 @@ /** 后端返回的用户权益相关类型 */ declare namespace ApiAuth { - /** 返回的token和刷新token */ + /** 返回的token */ interface Token { token: string; + } + /** 返回的token和刷新token */ + interface JWT { + token: string; refreshToken: string; } /** 返回的用户信息 */