From 21cf9227236f32054d7f6071cb301ff777468999 Mon Sep 17 00:00:00 2001 From: xingyu Date: Wed, 3 Aug 2022 00:41:59 +0800 Subject: [PATCH] perf: refresh token && delete console --- .../src/api/system/user/profile/index.ts | 4 +- .../src/components/Verifition/src/Verify.vue | 3 - .../Verifition/src/Verify/VerifySlide.vue | 1 - yudao-ui-admin-vue3/src/config/axios/index.ts | 76 ++++++++++++++----- yudao-ui-admin-vue3/src/router/index.ts | 11 +-- .../views/Profile/components/UserAvatar.vue | 56 +++++++------- .../src/views/system/menu/index.vue | 1 - .../src/views/system/oauth2/client/index.vue | 2 +- .../src/views/system/role/index.vue | 5 +- .../src/views/system/tenantPackage/index.vue | 4 +- 10 files changed, 100 insertions(+), 63 deletions(-) diff --git a/yudao-ui-admin-vue3/src/api/system/user/profile/index.ts b/yudao-ui-admin-vue3/src/api/system/user/profile/index.ts index a5a24490f..3eab59e01 100644 --- a/yudao-ui-admin-vue3/src/api/system/user/profile/index.ts +++ b/yudao-ui-admin-vue3/src/api/system/user/profile/index.ts @@ -24,6 +24,6 @@ export const updateUserPwdApi = (oldPassword: string, newPassword: string) => { } // 用户头像上传 -export const uploadAvatarApi = (params) => { - return request.upload({ url: '/system/user/profile/update-avatar', params }) +export const uploadAvatarApi = (data) => { + return request.upload({ url: '/system/user/profile/update-avatar', data: data }) } diff --git a/yudao-ui-admin-vue3/src/components/Verifition/src/Verify.vue b/yudao-ui-admin-vue3/src/components/Verifition/src/Verify.vue index 9fe0c5475..2e4af5cce 100644 --- a/yudao-ui-admin-vue3/src/components/Verifition/src/Verify.vue +++ b/yudao-ui-admin-vue3/src/components/Verifition/src/Verify.vue @@ -102,7 +102,6 @@ export default { * @description 刷新 * */ const refresh = () => { - console.log(instance.value) if (instance.value.refresh) { instance.value.refresh() } @@ -271,7 +270,6 @@ export default { -moz-box-sizing: content-box; box-sizing: content-box; border: 1px solid #ddd; - -webkit-border-radius: 4px; } .verify-bar-area .verify-move-block { @@ -284,7 +282,6 @@ export default { -moz-box-sizing: content-box; box-sizing: content-box; box-shadow: 0 0 2px #888888; - -webkit-border-radius: 1px; } .verify-bar-area .verify-move-block:hover { diff --git a/yudao-ui-admin-vue3/src/components/Verifition/src/Verify/VerifySlide.vue b/yudao-ui-admin-vue3/src/components/Verifition/src/Verify/VerifySlide.vue index a31f4587f..65136ff70 100644 --- a/yudao-ui-admin-vue3/src/components/Verifition/src/Verify/VerifySlide.vue +++ b/yudao-ui-admin-vue3/src/components/Verifition/src/Verify/VerifySlide.vue @@ -242,7 +242,6 @@ export default { //兼容移动端 var x = e.touches[0].pageX } - console.log(barArea) startLeft.value = Math.floor(x - barArea.value.getBoundingClientRect().left) startMoveTime.value = +new Date() //开始滑动的时间 if (isEnd.value == false) { diff --git a/yudao-ui-admin-vue3/src/config/axios/index.ts b/yudao-ui-admin-vue3/src/config/axios/index.ts index 839610595..123a7fbe8 100644 --- a/yudao-ui-admin-vue3/src/config/axios/index.ts +++ b/yudao-ui-admin-vue3/src/config/axios/index.ts @@ -1,10 +1,11 @@ import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios' -import { ElMessage, ElNotification } from 'element-plus' +import { ElMessage, ElMessageBox, ElNotification } from 'element-plus' import qs from 'qs' import { config } from '@/config/axios/config' -import { getAccessToken, getTenantId, removeToken } from '@/utils/auth' +import { getAccessToken, getRefreshToken, getTenantId, removeToken, setToken } from '@/utils/auth' import errorCode from './errorCode' import { useI18n } from '@/hooks/web/useI18n' +import { resetRouter } from '@/router' const tenantEnable = import.meta.env.VITE_APP_TENANT_ENABLE const BASE_URL = import.meta.env.VITE_BASE_URL @@ -20,9 +21,9 @@ const ignoreMsgs = [ export const isRelogin = { show: false } // Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现 // 请求队列 -// const requestList = [] +let requestList: any[] = [] // 是否正在刷新中 -// const isRefreshToken = false +let isRefreshToken = false export const PATH_URL = base_url[import.meta.env.VITE_API_BASEPATH] @@ -91,6 +92,7 @@ service.interceptors.request.use( service.interceptors.response.use( async (response: AxiosResponse) => { const { data } = response + console.info(data) if (!data) { // 返回“[HTTP]请求没有返回值”; throw new Error() @@ -112,16 +114,38 @@ service.interceptors.response.use( return Promise.reject(msg) } else if (code === 401) { // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了 - return handleAuthorized() - // if (!isRefreshToken) { - // isRefreshToken = true - // // 1. 如果获取不到刷新令牌,则只能执行登出操作 - // if (!getRefreshToken()) { - // return handleAuthorized() - // } - // // 2. 进行刷新访问令牌 - // // TODO: 引入refreshToken会循环依赖报错 - // } + if (!isRefreshToken) { + isRefreshToken = true + // 1. 如果获取不到刷新令牌,则只能执行登出操作 + if (!getRefreshToken()) { + return handleAuthorized() + } + // 2. 进行刷新访问令牌 + try { + const refreshTokenRes = await refreshToken() + // 2.1 刷新成功,则回放队列的请求 + 当前请求 + setToken(refreshTokenRes.data) + requestList.forEach((cb: any) => cb()) + return service(response.config) + } catch (e) { + // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。 + // 2.2 刷新失败,只回放队列的请求 + requestList.forEach((cb: any) => cb()) + // 提示是否要登出。即不回放当前请求!不然会形成递归 + return handleAuthorized() + } finally { + requestList = [] + isRefreshToken = false + } + } else { + // 添加到队列,等待刷新获取到新的令牌 + return new Promise((resolve) => { + requestList.push(() => { + ;(config as Recordable).headers.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改 + resolve(service(response.config)) + }) + }) + } } else if (code === 500) { ElMessage.error(t('sys.api.errMsg500')) return Promise.reject(new Error(msg)) @@ -165,14 +189,32 @@ service.interceptors.response.use( return Promise.reject(error) } ) + +const refreshToken = async () => { + return await service({ + url: '/system/auth/refresh-token?refreshToken=' + getRefreshToken(), + method: 'post' + }) +} const handleAuthorized = () => { const { t } = useI18n() if (!isRelogin.show) { - removeToken() isRelogin.show = true - ElNotification.error(t('sys.api.timeoutMessage')) + ElMessageBox.confirm(t('sys.api.timeoutMessage'), t('common.confirmTitle'), { + confirmButtonText: t('login.relogin'), + cancelButtonText: t('common.cancel'), + type: 'warning' + }) + .then(() => { + removeToken() + resetRouter() // 重置静态路由表 + isRelogin.show = false + location.href = '/' + }) + .catch(() => { + isRelogin.show = false + }) } - location.href = '/' return Promise.reject(t('sys.api.timeoutMessage')) } export { service } diff --git a/yudao-ui-admin-vue3/src/router/index.ts b/yudao-ui-admin-vue3/src/router/index.ts index 812ec4f7a..022042eeb 100644 --- a/yudao-ui-admin-vue3/src/router/index.ts +++ b/yudao-ui-admin-vue3/src/router/index.ts @@ -10,6 +10,7 @@ import { createRouter, createWebHashHistory } from 'vue-router' import { usePermissionStoreWithOut } from '@/store/modules/permission' import { useDictStoreWithOut } from '@/store/modules/dict' import { listSimpleDictDataApi } from '@/api/system/dict/dict.data' +import { isRelogin } from '@/config/axios' const permissionStore = usePermissionStoreWithOut() @@ -48,14 +49,14 @@ router.beforeEach(async (to, from, next) => { next({ path: '/' }) } else { if (!dictStore.getIsSetDict) { + isRelogin.show = true // 获取所有字典 const res = await listSimpleDictDataApi() - if (res) { - dictStore.setDictMap(res) - dictStore.setIsSetDict(true) - } + dictStore.setDictMap(res) + dictStore.setIsSetDict(true) } if (permissionStore.getIsAddRouters) { + isRelogin.show = false next() return } @@ -77,7 +78,7 @@ router.beforeEach(async (to, from, next) => { if (whiteList.indexOf(to.path) !== -1) { next() } else { - next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页 + next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页 } } }) diff --git a/yudao-ui-admin-vue3/src/views/Profile/components/UserAvatar.vue b/yudao-ui-admin-vue3/src/views/Profile/components/UserAvatar.vue index 4cdfb1484..b7a08de3a 100644 --- a/yudao-ui-admin-vue3/src/views/Profile/components/UserAvatar.vue +++ b/yudao-ui-admin-vue3/src/views/Profile/components/UserAvatar.vue @@ -6,12 +6,12 @@ import { ElRow, ElCol, ElUpload, ElMessage, ElDialog } from 'element-plus' import { propTypes } from '@/utils/propTypes' import { uploadAvatarApi } from '@/api/system/user/profile' const cropper = ref() +const dialogVisible = ref(false) +const cropperVisible = ref(false) const props = defineProps({ img: propTypes.string.def('') }) -const state = reactive({ - dialogVisible: false, - cropperVisible: false, +const options = reactive({ dialogTitle: '编辑头像', options: { img: props.img, //裁剪图片的地址 @@ -27,8 +27,11 @@ const state = reactive({ }) /** 编辑头像 */ const editCropper = () => { - state.dialogVisible = true - state.cropperVisible = true + dialogVisible.value = true +} +// 打开弹出层结束时的回调 +const modalOpened = () => { + cropperVisible.value = true } /** 向左旋转 */ const rotateLeft = () => { @@ -44,7 +47,7 @@ const changeScale = (num: number) => { cropper.value.changeScale(num) } // 覆盖默认的上传行为 -const requestUpload = () => {} +const requestUpload: any = () => {} /** 上传预处理 */ const beforeUpload = (file: Blob) => { if (file.type.indexOf('image/') == -1) { @@ -54,64 +57,65 @@ const beforeUpload = (file: Blob) => { reader.readAsDataURL(file) reader.onload = () => { if (reader.result) { - state.options.img = reader.result as string + options.options.img = reader.result as string } } } } /** 上传图片 */ const uploadImg = () => { - cropper.value.getCropBlob((data) => { + cropper.value.getCropBlob((data: any) => { let formData = new FormData() formData.append('avatarfile', data) uploadAvatarApi(formData) }) } /** 实时预览 */ -const realTime = (data) => { - state.previews = data +const realTime = (data: any) => { + options.previews = data } watch( () => props.img, () => { if (props.img) { - state.options.img = props.img - state.previews.img = props.img - state.previews.url = props.img + options.options.img = props.img + options.previews.img = props.img + options.previews.url = props.img } } )