Commit dc4c118afca81acdee8c67c46edc779a8e2dbaad
Merge branch 'dev-ww' into 'main_dev'
perf(share page): 优化分享页面的携带token影响当前用户在IOT平台操作 See merge request yunteng/thingskit-view!43
Showing
16 changed files
with
367 additions
and
49 deletions
| 1 | +import type { ErrorMessageMode } from '/#/external/axios'; | |
| 2 | + | |
| 3 | +export function checkStatus( | |
| 4 | + status: number, | |
| 5 | + msg: string, | |
| 6 | + errorMessageMode: ErrorMessageMode = 'message' | |
| 7 | +) { | |
| 8 | + let errMessage = msg; | |
| 9 | + switch (status) { | |
| 10 | + case 400: | |
| 11 | + errMessage = `${msg}`; | |
| 12 | + break; | |
| 13 | + // 401: Not logged in | |
| 14 | + // Jump to the login page if not logged in, and carry the path of the current page | |
| 15 | + // Return to the current page after successful login. This step needs to be operated on the login page. | |
| 16 | + case 401: | |
| 17 | + errMessage = '没有权限'; | |
| 18 | + break; | |
| 19 | + case 403: | |
| 20 | + errMessage = '未授权'; | |
| 21 | + break; | |
| 22 | + // 404请求不存在 | |
| 23 | + case 404: | |
| 24 | + errMessage = '未找到该资源!'; | |
| 25 | + break; | |
| 26 | + case 405: | |
| 27 | + errMessage = '网络请求错误,请求方法未允许!'; | |
| 28 | + break; | |
| 29 | + case 408: | |
| 30 | + errMessage = '网络请求超时!'; | |
| 31 | + break; | |
| 32 | + case 500: | |
| 33 | + errMessage = '服务器错误,请联系管理员!'; | |
| 34 | + break; | |
| 35 | + case 501: | |
| 36 | + errMessage = '网络未实现!'; | |
| 37 | + break; | |
| 38 | + case 502: | |
| 39 | + errMessage = '网络错误!'; | |
| 40 | + break; | |
| 41 | + case 503: | |
| 42 | + errMessage = '服务不可用,服务器暂时过载或维护!'; | |
| 43 | + break; | |
| 44 | + case 504: | |
| 45 | + errMessage = '网络超时!'; | |
| 46 | + break; | |
| 47 | + case 505: | |
| 48 | + errMessage = 'http版本不支持该请求!'; | |
| 49 | + break; | |
| 50 | + default: | |
| 51 | + } | |
| 52 | + | |
| 53 | + if (errMessage) { | |
| 54 | + if (errorMessageMode === 'message') { | |
| 55 | + const { error } = window['$message'] | |
| 56 | + error(errMessage); | |
| 57 | + } | |
| 58 | + } | |
| 59 | + return !!errMessage | |
| 60 | +} | ... | ... |
src/api/external/customRequest/http.ts
0 → 100644
| 1 | +import type { AxiosResponse } from 'axios'; | |
| 2 | +import type { RequestOptions, Result } from '/#/external/axios'; | |
| 3 | +import type { AxiosTransform, CreateAxiosOptions } from '@/utils/external/http/axios/axiosTransform'; | |
| 4 | +import { useGlobSetting } from '@/hooks/external/setting'; | |
| 5 | +import { RequestEnum, ContentTypeEnum } from '@/enums/external/httpEnum'; | |
| 6 | +import { isString } from '@/utils/external/is'; | |
| 7 | +import { getJwtToken, getShareJwtToken } from '@/utils/external/auth'; | |
| 8 | +import { setObjToUrlParams, deepMerge } from '@/utils/external'; | |
| 9 | +import { formatRequestDate, joinTimestamp } from '@/utils/external/http/axios/helper'; | |
| 10 | +import { VAxios } from '@/utils/external/http/axios/Axios'; | |
| 11 | +import { checkStatus } from './checkStatus'; | |
| 12 | + | |
| 13 | +const globSetting = useGlobSetting(); | |
| 14 | +const urlPrefix = globSetting.urlPrefix; | |
| 15 | + | |
| 16 | +/** | |
| 17 | + * @description: 数据处理,方便区分多种处理方式 | |
| 18 | + */ | |
| 19 | +const transform: AxiosTransform = { | |
| 20 | + /** | |
| 21 | + * @description: 处理请求数据。如果数据不是预期格式,可直接抛出错误 | |
| 22 | + */ | |
| 23 | + transformRequestHook: (res: AxiosResponse<Result>, options: RequestOptions) => { | |
| 24 | + const { isReturnNativeResponse } = options; | |
| 25 | + // 是否返回原生响应头 比如:需要获取响应头时使用该属性 | |
| 26 | + if (isReturnNativeResponse) { | |
| 27 | + return res; | |
| 28 | + } | |
| 29 | + return res.data; | |
| 30 | + }, | |
| 31 | + | |
| 32 | + // 请求之前处理config | |
| 33 | + beforeRequestHook: (config, options) => { | |
| 34 | + const { apiUrl, joinPrefix, joinParamsToUrl, formatDate, joinTime = true } = options; | |
| 35 | + | |
| 36 | + if (joinPrefix) { | |
| 37 | + config.url = `${urlPrefix}${config.url}`; | |
| 38 | + } | |
| 39 | + | |
| 40 | + if (apiUrl && isString(apiUrl)) { | |
| 41 | + config.url = `${apiUrl}${config.url}`; | |
| 42 | + } | |
| 43 | + | |
| 44 | + const params = config.params || {}; | |
| 45 | + const data = config.data || false; | |
| 46 | + formatDate && data && !isString(data) && formatRequestDate(data); | |
| 47 | + if (config.method?.toUpperCase() === RequestEnum.GET) { | |
| 48 | + if (!isString(params)) { | |
| 49 | + // 给 get 请求加上时间戳参数,避免从缓存中拿数据。 | |
| 50 | + config.params = Object.assign(params || {}, joinTimestamp(joinTime, false)); | |
| 51 | + } else { | |
| 52 | + // 兼容restful风格 | |
| 53 | + config.url = config.url + params + `${joinTimestamp(joinTime, true)}`; | |
| 54 | + config.params = undefined; | |
| 55 | + } | |
| 56 | + } else { | |
| 57 | + if (!isString(params)) { | |
| 58 | + formatDate && formatRequestDate(params); | |
| 59 | + if (Reflect.has(config, 'data') && config.data && Object.keys(config.data).length > 0) { | |
| 60 | + config.data = data; | |
| 61 | + config.params = params; | |
| 62 | + } else { | |
| 63 | + // 非GET请求如果没有提供data,则将params视为data | |
| 64 | + config.data = params; | |
| 65 | + config.params = undefined; | |
| 66 | + } | |
| 67 | + if (joinParamsToUrl) { | |
| 68 | + config.url = setObjToUrlParams( | |
| 69 | + config.url as string, | |
| 70 | + Object.assign({}, config.params, config.data) | |
| 71 | + ); | |
| 72 | + } | |
| 73 | + } else { | |
| 74 | + // 兼容restful风格 | |
| 75 | + config.url = config.url + params; | |
| 76 | + config.params = undefined; | |
| 77 | + } | |
| 78 | + } | |
| 79 | + return config; | |
| 80 | + }, | |
| 81 | + | |
| 82 | + /** | |
| 83 | + * @description: 请求拦截器处理 | |
| 84 | + */ | |
| 85 | + requestInterceptors: (config, options) => { | |
| 86 | + // 请求之前处理config | |
| 87 | + const { requestOptions } = config; | |
| 88 | + const { withShareToken } = requestOptions || {}; | |
| 89 | + const { requestOptions: { withToken } = {} } = options; | |
| 90 | + if (withToken !== false) { | |
| 91 | + const shareToken = getShareJwtToken(); | |
| 92 | + if (withShareToken && shareToken) { | |
| 93 | + config.headers!['X-Authorization'] = options.authenticationScheme | |
| 94 | + ? `${options.authenticationScheme} ${shareToken}` | |
| 95 | + : shareToken; | |
| 96 | + } else { | |
| 97 | + const token = getJwtToken(); | |
| 98 | + if (token) { | |
| 99 | + config.headers!['X-Authorization'] = options.authenticationScheme | |
| 100 | + ? `${options.authenticationScheme} ${token}` | |
| 101 | + : token; | |
| 102 | + } | |
| 103 | + } | |
| 104 | + } | |
| 105 | + return config | |
| 106 | + }, | |
| 107 | + | |
| 108 | + /** | |
| 109 | + * @description: 响应拦截器处理 | |
| 110 | + */ | |
| 111 | + responseInterceptors: (res: AxiosResponse<any>) => { | |
| 112 | + return res; | |
| 113 | + }, | |
| 114 | + | |
| 115 | + /** | |
| 116 | + * @description: 响应错误处理 | |
| 117 | + */ | |
| 118 | + responseInterceptorsCatch: (error: any) => { | |
| 119 | + | |
| 120 | + const { response, config } = error || {}; | |
| 121 | + const errorMessageMode = config?.requestOptions?.errorMessageMode || 'none'; | |
| 122 | + const errorMsgIsObj = typeof response?.data === 'object'; | |
| 123 | + const msg: string = errorMsgIsObj | |
| 124 | + ? response?.data?.message || response?.data?.msg | |
| 125 | + : response?.data; | |
| 126 | + | |
| 127 | + const flag = checkStatus(error?.response?.status, msg, errorMessageMode); | |
| 128 | + return Promise.reject(response?.data); | |
| 129 | + }, | |
| 130 | +}; | |
| 131 | + | |
| 132 | +function createAxios(opt?: Partial<CreateAxiosOptions>) { | |
| 133 | + return new VAxios( | |
| 134 | + deepMerge( | |
| 135 | + { | |
| 136 | + // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes | |
| 137 | + // authentication schemes,e.g: Bearer | |
| 138 | + // authenticationScheme: 'Bearer', | |
| 139 | + authenticationScheme: 'Bearer', | |
| 140 | + timeout: 10 * 1000, | |
| 141 | + // 基础接口地址 | |
| 142 | + // baseURL: globSetting.apiUrl, | |
| 143 | + // 接口可能会有通用的地址部分,可以统一抽取出来 | |
| 144 | + urlPrefix: urlPrefix, | |
| 145 | + headers: { 'Content-Type': ContentTypeEnum.JSON }, | |
| 146 | + // 如果是form-data格式 | |
| 147 | + // headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED }, | |
| 148 | + // 数据处理方式 | |
| 149 | + transform, | |
| 150 | + // 配置项,下面的选项都可以在独立的接口请求中覆盖 | |
| 151 | + requestOptions: { | |
| 152 | + // 默认将prefix 添加到url | |
| 153 | + joinPrefix: true, | |
| 154 | + // 是否返回原生响应头 比如:需要获取响应头时使用该属性 | |
| 155 | + isReturnNativeResponse: false, | |
| 156 | + // 需要对返回数据进行处理 | |
| 157 | + isTransformResponse: true, | |
| 158 | + // post请求的时候添加参数到url | |
| 159 | + joinParamsToUrl: false, | |
| 160 | + // 格式化提交参数时间 | |
| 161 | + formatDate: true, | |
| 162 | + // 消息提示类型 | |
| 163 | + errorMessageMode: 'message', | |
| 164 | + // 接口地址 | |
| 165 | + apiUrl: globSetting.apiUrl, | |
| 166 | + // 是否加入时间戳 | |
| 167 | + joinTime: true, | |
| 168 | + // 忽略重复请求 | |
| 169 | + ignoreCancelToken: true, | |
| 170 | + // 是否携带token | |
| 171 | + withToken: true | |
| 172 | + }, | |
| 173 | + }, | |
| 174 | + opt || {} | |
| 175 | + ) | |
| 176 | + ); | |
| 177 | +} | |
| 178 | +export const customHttp = createAxios(); | |
| 179 | + | ... | ... |
| 1 | 1 | import { RequestBodyEnum, RequestParams } from "@/enums/httpEnum" |
| 2 | -import { ExternalRequestType, ExtraRequestConfigType } from "@/store/external/modules/extraComponentInfo.d" | |
| 3 | -import { RequestConfigType, RequestGlobalConfigType } from "@/store/modules/chartEditStore/chartEditStore.d" | |
| 4 | -import { defHttp } from "@/utils/external/http/axios" | |
| 2 | +import { ExtraRequestConfigType } from "@/store/external/modules/extraComponentInfo.d" | |
| 3 | +import { RequestConfigType } from "@/store/modules/chartEditStore/chartEditStore.d" | |
| 4 | +import { isShareMode } from "@/views/share/hook" | |
| 5 | +import { customHttp } from "./http" | |
| 5 | 6 | |
| 6 | 7 | export enum ParamsType { |
| 7 | 8 | REQUIRED, |
| ... | ... | @@ -86,7 +87,7 @@ const transformBodyValue = (body: RequestParams['Body'], requestParamsBodyType: |
| 86 | 87 | |
| 87 | 88 | const extraValue = (object: Recordable) => { |
| 88 | 89 | return Object.keys(object).reduce((prev, next) => { |
| 89 | - return {...prev, ...(object[next] ? {[next]: object[next]} : {} )} | |
| 90 | + return { ...prev, ...(object[next] ? { [next]: object[next] } : {}) } | |
| 90 | 91 | }, {}) |
| 91 | 92 | } |
| 92 | 93 | |
| ... | ... | @@ -103,8 +104,8 @@ export const customRequest = async (request: RequestConfigType) => { |
| 103 | 104 | } |
| 104 | 105 | |
| 105 | 106 | const body = transformBodyValue(Body, requestParamsBodyType) |
| 106 | - | |
| 107 | - return defHttp.request<any>({ | |
| 107 | + | |
| 108 | + return customHttp.request<any>({ | |
| 108 | 109 | url: requestUrl, |
| 109 | 110 | baseURL: getOriginUrl(requestOriginUrl!), |
| 110 | 111 | method: requestHttpType, |
| ... | ... | @@ -113,6 +114,7 @@ export const customRequest = async (request: RequestConfigType) => { |
| 113 | 114 | headers: extraValue(Header) |
| 114 | 115 | }, { |
| 115 | 116 | joinPrefix: false, |
| 116 | - apiUrl: '' | |
| 117 | + apiUrl: '', | |
| 118 | + withShareToken: isShareMode() | |
| 117 | 119 | }) |
| 118 | 120 | } | ... | ... |
| 1 | 1 | import { ViewTypeEnum } from "@/enums/external/viewEnum" |
| 2 | 2 | import { defHttp } from "@/utils/external/http/axios" |
| 3 | +import { isShareMode } from "@/views/share/hook" | |
| 3 | 4 | |
| 4 | 5 | enum Api { |
| 5 | 6 | TOKEN = '/auth/login/public', |
| ... | ... | @@ -17,7 +18,7 @@ export const getPublicToken = (publicId: string) => { |
| 17 | 18 | return defHttp.post<Record<'token' | 'refreshToken', string>>({ |
| 18 | 19 | url: Api.TOKEN, |
| 19 | 20 | data: { publicId } |
| 20 | - }, { joinPrefix: false }) | |
| 21 | + }, { joinPrefix: false, withShareToken: isShareMode() }) | |
| 21 | 22 | } |
| 22 | 23 | |
| 23 | 24 | /** |
| ... | ... | @@ -27,7 +28,7 @@ export const getPublicToken = (publicId: string) => { |
| 27 | 28 | export const checkSharePageNeedAccessToken = (id: string) => { |
| 28 | 29 | return defHttp.get<{ data: boolean }>({ |
| 29 | 30 | url: `${Api.CHECK}/${ViewTypeEnum.LARGE_SCREEN}/${id}` |
| 30 | - }) | |
| 31 | + }, { withShareToken: isShareMode() }) | |
| 31 | 32 | } |
| 32 | 33 | |
| 33 | 34 | /** |
| ... | ... | @@ -39,5 +40,5 @@ export const getShareContentData = (options: Record<'id' | 'accessCredentials', |
| 39 | 40 | return defHttp.get<{ data: any }>({ |
| 40 | 41 | url: `${Api.SHARE_CONTENT}/${ViewTypeEnum.LARGE_SCREEN}/share_data/${options.id}`, |
| 41 | 42 | params: accessCredentials ? { accessCredentials } : {} |
| 42 | - }) | |
| 43 | + }, { withShareToken: isShareMode() }) | |
| 43 | 44 | } | ... | ... |
| ... | ... | @@ -5,6 +5,10 @@ export const JWT_TOKEN_KEY = 'JWT_TOKEN'; |
| 5 | 5 | |
| 6 | 6 | export const REFRESH_TOKEN_KEY = 'REFRESH_TOKEN'; |
| 7 | 7 | |
| 8 | +export const SHARE_JWT_TOKEN_KEY = 'SHARE_JWT_TOKEN'; | |
| 9 | + | |
| 10 | +export const SHARE_REFRESH_TOKEN_KEY = 'SHARE_REFRESH_TOKEN'; | |
| 11 | + | |
| 8 | 12 | export const LOCALE_KEY = 'LOCALE__'; |
| 9 | 13 | |
| 10 | 14 | // user info key | ... | ... |
| ... | ... | @@ -3,10 +3,11 @@ import { CreateComponentType } from "@/packages/index.d"; |
| 3 | 3 | import { useSocketStore } from "@/store/external/modules/socketStore"; |
| 4 | 4 | import { SocketReceiveMessageType } from "@/store/external/modules/socketStore.d"; |
| 5 | 5 | import { useChartEditStore } from "@/store/modules/chartEditStore/chartEditStore"; |
| 6 | -import { getJwtToken } from "@/utils/external/auth"; | |
| 6 | +import { getJwtToken, getShareJwtToken } from "@/utils/external/auth"; | |
| 7 | 7 | import { useWebSocket, WebSocketResult } from "@vueuse/core"; |
| 8 | 8 | import { onMounted, unref } from "vue"; |
| 9 | 9 | import { ExtraRequestConfigType } from "@/store/external/modules/extraComponentInfo.d"; |
| 10 | +import { isShareMode } from "@/views/share/hook"; | |
| 10 | 11 | |
| 11 | 12 | |
| 12 | 13 | interface SocketConnectionPoolType { |
| ... | ... | @@ -39,7 +40,7 @@ const getSocketInstance = (request: ExtraRequestConfigType) => { |
| 39 | 40 | if (~index) { |
| 40 | 41 | return socketConnectionPool[index].ws |
| 41 | 42 | } |
| 42 | - const token = getJwtToken() | |
| 43 | + const token = isShareMode() ? getShareJwtToken() : getJwtToken() | |
| 43 | 44 | const socketUrl = `${getOriginUrl(requestOriginUrl || '')}${requestUrl}?token=${token}` |
| 44 | 45 | |
| 45 | 46 | const instance = useWebSocket(socketUrl, { | ... | ... |
| ... | ... | @@ -3,7 +3,7 @@ import type { ErrorMessageMode } from '/#/external/axios'; |
| 3 | 3 | import { defineStore } from 'pinia'; |
| 4 | 4 | import { pinia as store } from '@/store'; |
| 5 | 5 | import { RoleEnum } from '@/enums/external/roleEnum'; |
| 6 | -import { JWT_TOKEN_KEY, REFRESH_TOKEN_KEY, ROLES_KEY, USER_INFO_KEY } from '@/enums/external/cacheEnum'; | |
| 6 | +import { JWT_TOKEN_KEY, REFRESH_TOKEN_KEY, ROLES_KEY, SHARE_JWT_TOKEN_KEY, SHARE_REFRESH_TOKEN_KEY, USER_INFO_KEY } from '@/enums/external/cacheEnum'; | |
| 7 | 7 | import { getAuthCache, getJwtToken, getRefreshToken, setAuthCache } from '@/utils/external/auth'; |
| 8 | 8 | import { |
| 9 | 9 | LoginParams, |
| ... | ... | @@ -29,6 +29,8 @@ interface UserState { |
| 29 | 29 | lastUpdateTime: number; |
| 30 | 30 | jwtToken?: string; |
| 31 | 31 | refreshToken?: string; |
| 32 | + shareJwtToken?: string; | |
| 33 | + shareRefreshToken?: string; | |
| 32 | 34 | outTarget?: string; |
| 33 | 35 | } |
| 34 | 36 | |
| ... | ... | @@ -47,6 +49,8 @@ export const useUserStore = defineStore({ |
| 47 | 49 | jwtToken: getJwtToken() as string || undefined, |
| 48 | 50 | //refresh Token |
| 49 | 51 | refreshToken: getRefreshToken() as string || undefined, |
| 52 | + shareJwtToken: undefined, | |
| 53 | + shareRefreshToken: undefined, | |
| 50 | 54 | // roleList |
| 51 | 55 | roleList: [], |
| 52 | 56 | // Whether the login expired |
| ... | ... | @@ -96,6 +100,12 @@ export const useUserStore = defineStore({ |
| 96 | 100 | setAuthCache(JWT_TOKEN_KEY, jwtToken); |
| 97 | 101 | setAuthCache(REFRESH_TOKEN_KEY, refreshToken); |
| 98 | 102 | }, |
| 103 | + storeShareToken(jwtToken: string, refreshToken: string) { | |
| 104 | + this.shareJwtToken = jwtToken; | |
| 105 | + this.shareRefreshToken = refreshToken; | |
| 106 | + setAuthCache(SHARE_JWT_TOKEN_KEY, jwtToken); | |
| 107 | + setAuthCache(SHARE_REFRESH_TOKEN_KEY, refreshToken); | |
| 108 | + }, | |
| 99 | 109 | setToken(info: string | undefined) { |
| 100 | 110 | this.jwtToken = info; |
| 101 | 111 | setAuthCache(JWT_TOKEN_KEY, info); |
| ... | ... | @@ -152,14 +162,6 @@ export const useUserStore = defineStore({ |
| 152 | 162 | } |
| 153 | 163 | return userInfo; |
| 154 | 164 | }, |
| 155 | - async smsCodelogin( | |
| 156 | - params: SmsLoginParams & { | |
| 157 | - goHome?: boolean; | |
| 158 | - mode?: ErrorMessageMode; | |
| 159 | - } | |
| 160 | - ) { | |
| 161 | - | |
| 162 | - }, | |
| 163 | 165 | async getMyUserInfoAction() { |
| 164 | 166 | const userInfo = await getMyInfo(); |
| 165 | 167 | this.setUserInfo(userInfo); |
| ... | ... | @@ -193,6 +195,20 @@ export const useUserStore = defineStore({ |
| 193 | 195 | }, |
| 194 | 196 | |
| 195 | 197 | /** |
| 198 | + * @description 刷新分享页面的token | |
| 199 | + */ | |
| 200 | + async doShareRefresh() { | |
| 201 | + try { | |
| 202 | + const req = { refreshToken: this.shareRefreshToken } as RefreshTokenParams | |
| 203 | + const { token, refreshToken } = await doRefreshToken(req) | |
| 204 | + this.storeShareToken(token, refreshToken) | |
| 205 | + } catch (error) { | |
| 206 | + window.location.reload() | |
| 207 | + throw error | |
| 208 | + } | |
| 209 | + }, | |
| 210 | + | |
| 211 | + /** | |
| 196 | 212 | * @description: Confirm before logging out |
| 197 | 213 | */ |
| 198 | 214 | confirmLoginOut() { | ... | ... |
| 1 | 1 | import { Persistent, BasicKeys } from '@/utils/external/cache/persistent'; |
| 2 | -import { CacheTypeEnum } from '@/enums/external/cacheEnum'; | |
| 2 | +import { CacheTypeEnum, SHARE_JWT_TOKEN_KEY, SHARE_REFRESH_TOKEN_KEY } from '@/enums/external/cacheEnum'; | |
| 3 | 3 | import projectSetting from '@/settings/external/projectSetting'; |
| 4 | 4 | import { JWT_TOKEN_KEY, REFRESH_TOKEN_KEY } from '@/enums/external/cacheEnum'; |
| 5 | 5 | |
| ... | ... | @@ -20,9 +20,17 @@ export function clearAuthCache(immediate = true) { |
| 20 | 20 | const fn = isLocal ? Persistent.clearLocal : Persistent.clearSession; |
| 21 | 21 | return fn(immediate); |
| 22 | 22 | } |
| 23 | -export function getJwtToken() { | |
| 23 | +export function getJwtToken(): string { | |
| 24 | 24 | return getAuthCache(JWT_TOKEN_KEY); |
| 25 | 25 | } |
| 26 | -export function getRefreshToken() { | |
| 26 | +export function getRefreshToken(): string { | |
| 27 | 27 | return getAuthCache(REFRESH_TOKEN_KEY); |
| 28 | 28 | } |
| 29 | + | |
| 30 | +export function getShareJwtToken(): string { | |
| 31 | + return getAuthCache(SHARE_JWT_TOKEN_KEY); | |
| 32 | +} | |
| 33 | +export function getShareRefreshToken(): string { | |
| 34 | + return getAuthCache(SHARE_REFRESH_TOKEN_KEY); | |
| 35 | +} | |
| 36 | + | ... | ... |
| ... | ... | @@ -17,9 +17,11 @@ import { |
| 17 | 17 | APP_SESSION_CACHE_KEY, |
| 18 | 18 | MULTIPLE_TABS_KEY, |
| 19 | 19 | MENU_LIST, |
| 20 | + SHARE_JWT_TOKEN_KEY, | |
| 21 | + SHARE_REFRESH_TOKEN_KEY, | |
| 20 | 22 | } from '@/enums/external/cacheEnum'; |
| 21 | 23 | import { DEFAULT_CACHE_TIME } from '@/settings/external/encryptionSetting'; |
| 22 | -import { toRaw } from 'vue'; | |
| 24 | +import { toRaw } from 'vue'; | |
| 23 | 25 | import omit from 'lodash/omit'; |
| 24 | 26 | import pick from 'lodash/pick'; |
| 25 | 27 | |
| ... | ... | @@ -34,6 +36,8 @@ interface BasicStore { |
| 34 | 36 | [PROJ_CFG_KEY]: ProjectConfig; |
| 35 | 37 | [MULTIPLE_TABS_KEY]: RouteLocationNormalized[]; |
| 36 | 38 | [MENU_LIST]: any[]; |
| 39 | + [SHARE_JWT_TOKEN_KEY]: string | |
| 40 | + [SHARE_REFRESH_TOKEN_KEY]: string | |
| 37 | 41 | } |
| 38 | 42 | |
| 39 | 43 | type LocalStore = BasicStore; | ... | ... |
| 1 | -import type { AxiosRequestConfig, AxiosInstance, AxiosResponse, AxiosError } from 'axios'; | |
| 2 | -import type { CreateAxiosOptions } from './axiosTransform'; | |
| 1 | +import type { AxiosInstance, AxiosResponse, AxiosError } from 'axios'; | |
| 2 | +import type { AxiosRequestConfig, CreateAxiosOptions } from './axiosTransform'; | |
| 3 | 3 | import axios from 'axios'; |
| 4 | 4 | import qs from 'qs'; |
| 5 | 5 | import { AxiosCanceler } from './axiosCancel'; |
| ... | ... | @@ -115,13 +115,21 @@ export class VAxios { |
| 115 | 115 | this.axiosInstance.interceptors.request.use(async (config: AxiosRequestConfig) => { |
| 116 | 116 | // If cancel repeat request is turned on, then cancel repeat request is prohibited |
| 117 | 117 | const userStore = useUserStore(); |
| 118 | - if (userStore && userStore.jwtToken) { | |
| 118 | + // if (userStore && (userStore.jwtToken || userStore.shareJwtToken)) { | |
| 119 | + if (userStore) { | |
| 119 | 120 | try { |
| 120 | - const res = jwt_decode(userStore.jwtToken) as JwtModel; | |
| 121 | - const currentTime = (new Date().getTime() + (config.timeout || 0)) / 1000; | |
| 122 | - if (currentTime >= res.exp && this.isNeedTokenURL(config.url!)) { | |
| 123 | - await this.refreshTokenBeforeReq(userStore.doRefresh); | |
| 121 | + const { requestOptions = {} } = config | |
| 122 | + const { withShareToken, withToken } = requestOptions | |
| 123 | + const token = withShareToken && withToken ? userStore.shareJwtToken : userStore.jwtToken | |
| 124 | + const doRefresh = withShareToken && withToken ? userStore.doShareRefresh : userStore.doRefresh | |
| 125 | + if (token) { | |
| 126 | + const res = jwt_decode(token) as JwtModel; | |
| 127 | + const currentTime = (new Date().getTime() + (config.timeout || 0)) / 1000; | |
| 128 | + if (currentTime >= res.exp && this.isNeedTokenURL(config.url!)) { | |
| 129 | + await this.refreshTokenBeforeReq(doRefresh); | |
| 130 | + } | |
| 124 | 131 | } |
| 132 | + | |
| 125 | 133 | } catch (error) { |
| 126 | 134 | userStore.logout(); |
| 127 | 135 | } | ... | ... |
| 1 | 1 | /** |
| 2 | 2 | * Data processing class, can be configured according to the project |
| 3 | 3 | */ |
| 4 | -import type { AxiosRequestConfig, AxiosResponse } from 'axios'; | |
| 4 | +import type { AxiosRequestConfig as OriginAxiosRequestConfig, AxiosResponse } from 'axios'; | |
| 5 | 5 | import type { RequestOptions, Result } from '/#/external/axios'; |
| 6 | 6 | |
| 7 | 7 | export interface CreateAxiosOptions extends AxiosRequestConfig { |
| ... | ... | @@ -11,6 +11,8 @@ export interface CreateAxiosOptions extends AxiosRequestConfig { |
| 11 | 11 | requestOptions?: RequestOptions; |
| 12 | 12 | } |
| 13 | 13 | |
| 14 | +export type AxiosRequestConfig = OriginAxiosRequestConfig & {requestOptions?: RequestOptions} | |
| 15 | + | |
| 14 | 16 | export abstract class AxiosTransform { |
| 15 | 17 | /** |
| 16 | 18 | * @description: Process configuration before request | ... | ... |
| ... | ... | @@ -9,7 +9,7 @@ import { checkStatus } from './checkStatus'; |
| 9 | 9 | import { useGlobSetting } from '@/hooks/external/setting'; |
| 10 | 10 | import { RequestEnum, ContentTypeEnum } from '@/enums/external/httpEnum'; |
| 11 | 11 | import { isString } from '@/utils/external/is'; |
| 12 | -import { getJwtToken } from '@/utils/external/auth'; | |
| 12 | +import { getJwtToken, getShareJwtToken } from '@/utils/external/auth'; | |
| 13 | 13 | import { setObjToUrlParams, deepMerge } from '@/utils/external'; |
| 14 | 14 | import { joinTimestamp, formatRequestDate } from './helper'; |
| 15 | 15 | import { PageEnum } from '@/enums/external/pageEnum'; |
| ... | ... | @@ -89,14 +89,25 @@ const transform: AxiosTransform = { |
| 89 | 89 | */ |
| 90 | 90 | requestInterceptors: (config, options) => { |
| 91 | 91 | // 请求之前处理config |
| 92 | - const token = getJwtToken(); | |
| 93 | - if (token && (config as Recordable)?.requestOptions?.withToken !== false) { | |
| 94 | - // jwt token | |
| 95 | - config.headers!['X-Authorization'] = options.authenticationScheme | |
| 96 | - ? `${options.authenticationScheme} ${token}` | |
| 97 | - : token as string; | |
| 92 | + const { requestOptions } = config; | |
| 93 | + const { withShareToken } = requestOptions || {}; | |
| 94 | + const { requestOptions: { withToken } = {} } = options; | |
| 95 | + if (withToken !== false) { | |
| 96 | + const shareToken = getShareJwtToken(); | |
| 97 | + if (withShareToken && shareToken) { | |
| 98 | + config.headers!['X-Authorization'] = options.authenticationScheme | |
| 99 | + ? `${options.authenticationScheme} ${shareToken}` | |
| 100 | + : shareToken; | |
| 101 | + } else { | |
| 102 | + const token = getJwtToken(); | |
| 103 | + if (token) { | |
| 104 | + config.headers!['X-Authorization'] = options.authenticationScheme | |
| 105 | + ? `${options.authenticationScheme} ${token}` | |
| 106 | + : token; | |
| 107 | + } | |
| 108 | + } | |
| 98 | 109 | } |
| 99 | - return config; | |
| 110 | + return config | |
| 100 | 111 | }, |
| 101 | 112 | |
| 102 | 113 | /** | ... | ... |
src/views/share/hook/index.ts
0 → 100644
| ... | ... | @@ -5,10 +5,23 @@ |
| 5 | 5 | <NCard> |
| 6 | 6 | <NForm @keyup.enter="handleSubmit"> |
| 7 | 7 | <NFormItem label="访问令牌"> |
| 8 | - <NInput v-model:value="accessCredentials" type="password" /> | |
| 9 | - </NFormItem> | |
| 10 | - <NFormItem :showLabel="false"> | |
| 11 | - <NButton :loading="loading" type="primary" style="width: 100%;" @click="handleSubmit">访问</NButton> | |
| 8 | + <NInputGroup> | |
| 9 | + <NInput v-model:value="accessCredentials" show-password-on="mousedown" type="password" | |
| 10 | + style="width: 70%;" /> | |
| 11 | + <NButton :loading="loading" type="primary" style="width: 30%;" @click="handleSubmit"> | |
| 12 | + <svg t="1682068997810" class="icon" viewBox="0 0 1024 1024" version="1.1" | |
| 13 | + xmlns="http://www.w3.org/2000/svg" p-id="79416" width="24" height="24"> | |
| 14 | + <path | |
| 15 | + d="M512 928H128c-19.2 0-32-12.8-32-32V128c0-19.2 12.8-32 32-32h384c19.2 0 32 12.8 32 32s-12.8 32-32 32H160v704h352c19.2 0 32 12.8 32 32s-12.8 32-32 32z" | |
| 16 | + fill="#fff" p-id="79417" /> | |
| 17 | + <path | |
| 18 | + d="M534.4 736c-9.6 0-16-3.2-22.4-9.6l-192-192c-12.8-12.8-12.8-32 0-44.8l192-192c12.8-12.8 32-12.8 44.8 0 12.8 12.8 12.8 32 0 44.8L387.2 512l169.6 169.6c12.8 12.8 12.8 32 0 44.8-6.4 6.4-16 9.6-22.4 9.6z" | |
| 19 | + fill="#fff" p-id="79418" /> | |
| 20 | + <path d="M896 544H342.4c-19.2 0-32-12.8-32-32s12.8-32 32-32H896c19.2 0 32 12.8 32 32s-12.8 32-32 32z" | |
| 21 | + fill="#fff" p-id="79419" /> | |
| 22 | + </svg> | |
| 23 | + </NButton> | |
| 24 | + </NInputGroup> | |
| 12 | 25 | </NFormItem> |
| 13 | 26 | </NForm> |
| 14 | 27 | </NCard> |
| ... | ... | @@ -17,11 +30,11 @@ |
| 17 | 30 | </template> |
| 18 | 31 | |
| 19 | 32 | <script setup lang="ts"> |
| 20 | -import { NModal, NCard, NForm, NFormItem, NInput, NButton, } from 'naive-ui' | |
| 33 | +import { NModal, NCard, NForm, NFormItem, NInput, NButton, NInputGroup } from 'naive-ui' | |
| 21 | 34 | import { getSessionStorageInfo } from '../preview/utils' |
| 22 | 35 | import type { ChartEditStorageType } from '../preview/index.d' |
| 23 | 36 | import { SavePageEnum } from '@/enums/editPageEnum' |
| 24 | -import { JSONParse, setSessionStorage } from '@/utils' | |
| 37 | +import { JSONParse, setSessionStorage, setTitle } from '@/utils' | |
| 25 | 38 | import { StorageEnum } from '@/enums/storageEnum' |
| 26 | 39 | import { onMounted, ref, unref } from 'vue' |
| 27 | 40 | import Preview from '../preview/index.vue' |
| ... | ... | @@ -42,7 +55,7 @@ const getToken = async () => { |
| 42 | 55 | const { params } = ROUTE |
| 43 | 56 | const { publicId } = params as Record<'id' | 'publicId', string> |
| 44 | 57 | const { token, refreshToken } = await getPublicToken(publicId) |
| 45 | - userStore.storeToken(token, refreshToken) | |
| 58 | + userStore.storeShareToken(token, refreshToken) | |
| 46 | 59 | } |
| 47 | 60 | |
| 48 | 61 | const checkNeedAccessToken = async () => { |
| ... | ... | @@ -77,6 +90,7 @@ const getSharePageContentData = async () => { |
| 77 | 90 | chartEditStore.requestGlobalConfig = requestGlobalConfig |
| 78 | 91 | chartEditStore.componentList = componentList |
| 79 | 92 | } |
| 93 | + setTitle(dataViewName || '') | |
| 80 | 94 | showModal.value = false |
| 81 | 95 | allowLoadPreviewPage.value = false |
| 82 | 96 | } catch (error) { | ... | ... |