ref : 重构令牌管理逻辑,,统一状态码识别

This commit is contained in:
TsMask
2025-04-27 17:23:33 +08:00
parent f76311cf1b
commit 2de9788373
23 changed files with 229 additions and 253 deletions

View File

@@ -1,25 +1,45 @@
import Cookies from 'js-cookie';
import { TOKEN_COOKIE } from '@/constants/token-constants';
import { localRemove, localSet } from '@/utils/cache-local-utils';
import {
CACHE_LOCAL_LOCK_PASSWD,
CACHE_LOCAL_MASK,
} from '@/constants/cache-keys-constants';
TOKEN_ACCESS_COOKIE,
TOKEN_REFRESH_COOKIE,
} from '@/constants/token-constants';
/**获取cookis中Token字符串 */
export function getToken(): string {
return Cookies.get(TOKEN_COOKIE) || '';
/**获取访问令牌 */
export function getAccessToken(): string {
return Cookies.get(TOKEN_ACCESS_COOKIE) || '';
}
/**设置cookis中Token字符串 */
export function setToken(token: string): void {
Cookies.set(TOKEN_COOKIE, token || '');
localSet(CACHE_LOCAL_MASK, 'none');
/**
* 设置访问令牌
* @param token token字符串
* @param exp 过期时间(秒)
*/
export function setAccessToken(token: string, exp: number): void {
const expires = new Date(new Date().getTime() + exp * 1000);
Cookies.set(TOKEN_ACCESS_COOKIE, token, { expires });
}
/**移除cookis中Token字符串,localStorage中锁屏字符串 */
export function removeToken(): void {
Cookies.remove(TOKEN_COOKIE);
localRemove(CACHE_LOCAL_MASK);
localRemove(CACHE_LOCAL_LOCK_PASSWD);
/**移除访问令牌 */
export function delAccessToken(): void {
Cookies.remove(TOKEN_ACCESS_COOKIE);
}
/**获取刷新令牌 */
export function getRefreshToken(): string {
return Cookies.get(TOKEN_REFRESH_COOKIE) || '';
}
/**
* 设置刷新令牌
* @param token token字符串
* @param exp 过期时间(秒)
*/
export function setRefreshToken(token: string, exp: number): void {
const expires = new Date(new Date().getTime() + exp * 1000);
Cookies.set(TOKEN_REFRESH_COOKIE, token, { expires });
}
/**移除刷新令牌 */
export function delRefreshToken(): void {
Cookies.remove(TOKEN_REFRESH_COOKIE);
}

View File

@@ -1,10 +1,16 @@
import { getToken, removeToken } from '@/plugins/auth-token';
import {
getAccessToken,
setAccessToken,
delAccessToken,
getRefreshToken,
setRefreshToken,
delRefreshToken,
} from '@/plugins/auth-token';
import {
sessionGet,
sessionGetJSON,
sessionSetJSON,
} from '@/utils/cache-session-utils';
import { localGet } from '@/utils/cache-local-utils';
import { TOKEN_KEY, TOKEN_KEY_PREFIX } from '@/constants/token-constants';
import {
CACHE_LOCAL_I18N,
@@ -18,6 +24,7 @@ import {
import {
RESULT_CODE_ENCRYPT,
RESULT_CODE_ERROR,
RESULT_CODE_EXCEPTION,
RESULT_CODE_SUCCESS,
RESULT_MSG_ENCRYPT,
RESULT_MSG_ERROR,
@@ -25,10 +32,11 @@ import {
RESULT_MSG_SERVER_ERROR,
RESULT_MSG_SUCCESS,
RESULT_MSG_TIMEOUT,
RESULT_MSG_URL_NOTFOUND,
RESULT_MSG_URL_RESUBMIT,
} from '@/constants/result-constants';
import { decryptAES, encryptAES } from '@/utils/encrypt-utils';
import { localGet } from '@/utils/cache-local-utils';
import { refreshToken } from '@/api/auth';
/**响应结果类型 */
export type ResultType = {
@@ -61,7 +69,7 @@ type OptionsType = {
/**请求地址 */
url: string;
/**请求方法 */
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
/**请求头 */
headers?: HeadersInit;
/**地址栏参数 */
@@ -133,16 +141,21 @@ function beforeRequest(options: OptionsType): OptionsType | Promise<any> {
Reflect.set(options.headers, 'Accept-Language', `${language};q=0.9`);
// 是否需要设置 token
const token = getToken();
if (options.whithToken && token) {
Reflect.set(options.headers, TOKEN_KEY, TOKEN_KEY_PREFIX + token);
const accessToken = getAccessToken();
if (options.whithToken && accessToken) {
Reflect.set(
options.headers,
TOKEN_KEY,
TOKEN_KEY_PREFIX + ' ' + accessToken
);
}
// 是否需要防止数据重复提交
if (
options.repeatSubmit &&
options.dataType === 'json' &&
['post', 'put'].includes(options.method)
!(options.data instanceof FormData) &&
['POST', 'PUT'].includes(options.method)
) {
const requestObj: RepeatSubmitType = {
url: options.url,
@@ -212,13 +225,31 @@ function beforeRequest(options: OptionsType): OptionsType | Promise<any> {
return options;
}
/**请求后的拦截 */
function interceptorResponse(res: ResultType): ResultType | Promise<any> {
/**响应前的拦截 */
async function beforeResponse(
options: OptionsType,
res: ResultType
): Promise<any> {
// console.log('请求后的拦截', res);
// 登录失效时,移除授权令牌并重新刷新页面
if (res.code === 401) {
removeToken();
// 登录失效时,移除访问令牌并重新请求
if (res.code === 401001) {
const result = await refreshToken(getRefreshToken());
// 更新访问令牌和刷新令牌
if (result.code === RESULT_CODE_SUCCESS) {
setAccessToken(result.data.accessToken, result.data.refreshExpiresIn);
setRefreshToken(result.data.refreshToken, result.data.refreshExpiresIn);
return await request(options);
} else {
delAccessToken();
delRefreshToken();
window.location.reload();
}
}
if ([401002, 401003].includes(res.code)) {
delAccessToken();
delRefreshToken();
window.location.reload();
}
@@ -271,43 +302,45 @@ function interceptorResponse(res: ResultType): ResultType | Promise<any> {
* @returns 返回 Promise<ResultType>
*/
export async function request(options: OptionsType): Promise<ResultType> {
options = Object.assign({}, FATCH_OPTIONS, options);
let timeoutId: any = 0;
let reqOptions = Object.assign({}, FATCH_OPTIONS, options);
// 请求超时控制请求终止
if (!options.signal) {
let timeoutId: any = null;
if (!reqOptions.signal) {
const controller = new AbortController();
const { signal } = controller;
options.signal = signal;
reqOptions.signal = controller.signal;
timeoutId = setTimeout(() => {
controller.abort(); // 终止请求
}, options.timeout);
}, reqOptions.timeout);
}
// 检查请求拦截
const beforeReq = beforeRequest(options);
const beforeReq = beforeRequest(reqOptions);
if (beforeReq instanceof Promise) {
return await beforeReq;
}
options = beforeReq;
reqOptions = beforeReq;
// 判断用户传递的URL是否http或/开头
if (!options.url.startsWith('http')) {
const uri = options.url.startsWith('/') ? options.url : `/${options.url}`;
options.url = options.baseUrl + uri;
if (!reqOptions.url.startsWith('http')) {
const uri = reqOptions.url.startsWith('/')
? reqOptions.url
: `/${reqOptions.url}`;
reqOptions.url = reqOptions.baseUrl + uri;
}
try {
const res = await fetch(options.url, options);
const res = await fetch(reqOptions.url, reqOptions);
// console.log('请求结果:', res);
// 状态码拦截处理
const reqNot = stateCode(res);
if (reqNot != false) {
return reqNot;
if (res.status === 500) {
return {
code: RESULT_CODE_EXCEPTION,
msg: RESULT_MSG_SERVER_ERROR[language],
};
}
// 根据响应数据类型返回
switch (options.responseType) {
switch (reqOptions.responseType) {
case 'text': // 文本数据
const str = await res.text();
return {
@@ -317,11 +350,7 @@ export async function request(options: OptionsType): Promise<ResultType> {
case 'json': // json格式数据
const result = await res.json();
// 请求后的拦截
const beforeRes = interceptorResponse(result);
if (beforeRes instanceof Promise) {
return await beforeRes;
}
return result;
return await beforeResponse(options, result);
case 'blob': // 二进制数据则直接返回
case 'arrayBuffer':
const contentType = res.headers.get('content-type') || '';
@@ -330,7 +359,7 @@ export async function request(options: OptionsType): Promise<ResultType> {
return result as ResultType;
}
const data =
options.responseType === 'blob'
reqOptions.responseType === 'blob'
? await res.blob()
: await res.arrayBuffer();
return {
@@ -356,41 +385,7 @@ export async function request(options: OptionsType): Promise<ResultType> {
}
throw error;
} finally {
clearTimeout(timeoutId); // 请求成功,清除超时计时器
clearTimeout(timeoutId); // 清除超时计时器
timeoutId = null;
}
}
/**
* 判断状态码处理结果信息(不可处理)
* @param res 请求结果
* @returns
*/
function stateCode(res: Response) {
// 网络异常
if (res.status === 500) {
return {
code: RESULT_CODE_ERROR,
msg: RESULT_MSG_SERVER_ERROR[language],
};
}
// 上传文件成功无内容返回
if (res.status === 204 || res.statusText === 'No Content') {
return {
code: RESULT_CODE_SUCCESS,
msg: RESULT_MSG_SUCCESS[language],
};
}
// 地址找不到
if (res.status === 404 || res.status === 405) {
return {
code: RESULT_CODE_ERROR,
msg: RESULT_MSG_URL_NOTFOUND[language],
};
}
// 身份授权
if (res.status === 401) {
removeToken();
window.location.reload();
}
return false;
}

View File

@@ -1,5 +1,5 @@
import { sessionGet } from '@/utils/cache-session-utils';
import { getToken } from './auth-token';
import { getAccessToken } from './auth-token';
import { localGet } from '@/utils/cache-local-utils';
import { CACHE_LOCAL_I18N } from '@/constants/cache-keys-constants';
import { TOKEN_RESPONSE_FIELD } from '@/constants/token-constants';
@@ -92,7 +92,7 @@ export class WS {
// 地址栏参数
let params = Object.assign({}, options.params, {
// 设置 token
[TOKEN_RESPONSE_FIELD]: getToken(),
[TOKEN_RESPONSE_FIELD]: getAccessToken(),
// 多语言
['language']: localGet(CACHE_LOCAL_I18N) || 'en_US',
});