fix: bbs与ai存储refreshToken
This commit is contained in:
@@ -12,8 +12,8 @@ VITE_WEB_BASE_API = '/dev-api'
|
||||
# VITE_WEB_BASE_API='/prod-api'
|
||||
|
||||
# 本地接口
|
||||
VITE_API_URL = http://localhost:19001/api/app
|
||||
# VITE_API_URL=http://ccnetcore.com:19001/api/app
|
||||
# VITE_API_URL = http://localhost:19001/api/app
|
||||
VITE_API_URL=http://ccnetcore.com:19001/api/app
|
||||
|
||||
# SSO单点登录url
|
||||
VITE_SSO_SEVER_URL='http://localhost:18001'
|
||||
|
||||
@@ -3,6 +3,9 @@ import { post } from '@/utils/request';
|
||||
|
||||
export const login = (data: LoginDTO) => post<LoginVO>('/auth/login', data).json();
|
||||
|
||||
// 刷新token
|
||||
export const updateToken = (data: any) => post<any>('/account/refresh', data).json();
|
||||
|
||||
// 邮箱验证码
|
||||
export const emailCode = (data: EmailCodeDTO) => post('/resource/email/code', data).json();
|
||||
|
||||
|
||||
@@ -76,8 +76,8 @@ function handleThirdPartyLogin() {
|
||||
try {
|
||||
// 清理监听
|
||||
window.removeEventListener('message', messageHandler);
|
||||
const { token } = event.data;
|
||||
userStore.setToken(token);
|
||||
const { token, refreshToken } = event.data;
|
||||
userStore.setToken(token, refreshToken);
|
||||
const resUserInfo = await getUserInfo();
|
||||
userStore.setUserInfo(resUserInfo.data);
|
||||
// 关闭弹窗
|
||||
|
||||
@@ -6,12 +6,20 @@ export const useUserStore = defineStore(
|
||||
'user',
|
||||
() => {
|
||||
const token = ref<string>();
|
||||
const refreshToken = ref<string | undefined>();
|
||||
const router = useRouter();
|
||||
const setToken = (value: string) => {
|
||||
const setToken = (value: string, refreshValue?: string) => {
|
||||
// 让接口报401
|
||||
// token.value = `${value}cdsfds`;
|
||||
|
||||
token.value = value;
|
||||
if (refreshValue) {
|
||||
refreshToken.value = refreshValue;
|
||||
}
|
||||
};
|
||||
const clearToken = () => {
|
||||
token.value = void 0;
|
||||
refreshToken.value = void 0;
|
||||
};
|
||||
|
||||
const userInfo = ref<LoginUser>();
|
||||
@@ -44,6 +52,7 @@ export const useUserStore = defineStore(
|
||||
|
||||
return {
|
||||
token,
|
||||
refreshToken,
|
||||
setToken,
|
||||
clearToken,
|
||||
userInfo,
|
||||
|
||||
@@ -1,26 +1,21 @@
|
||||
import type { HookFetchPlugin } from 'hook-fetch';
|
||||
import FingerprintJS from '@fingerprintjs/fingerprintjs'; // 新增指纹库
|
||||
import FingerprintJS from '@fingerprintjs/fingerprintjs';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import hookFetch from 'hook-fetch';
|
||||
import { sseTextDecoderPlugin } from 'hook-fetch/plugins';
|
||||
import router from '@/routers';
|
||||
import { useUserStore } from '@/stores';
|
||||
|
||||
// 初始化指纹(单例模式)
|
||||
// 初始化指纹
|
||||
const fpPromise = FingerprintJS.load();
|
||||
|
||||
// 获取浏览器指纹(缓存结果)
|
||||
async function getFingerprint(): Promise<string> {
|
||||
const fp = await fpPromise;
|
||||
const { visitorId } = await fp.get();
|
||||
return visitorId;
|
||||
return (await fp.get()).visitorId;
|
||||
}
|
||||
|
||||
// 标准响应格式
|
||||
interface BaseResponse<T = any> {
|
||||
code: number;
|
||||
code?: number; // 变为可选字段
|
||||
data: T;
|
||||
msg: string;
|
||||
msg?: string;
|
||||
}
|
||||
|
||||
declare module 'hook-fetch' {
|
||||
@@ -29,6 +24,10 @@ declare module 'hook-fetch' {
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新控制变量
|
||||
let isRefreshing = false;
|
||||
let pendingRequests: (() => void)[] = [];
|
||||
|
||||
export const request = hookFetch.create<BaseResponse>({
|
||||
baseURL: import.meta.env.VITE_WEB_BASE_API,
|
||||
headers: {
|
||||
@@ -37,7 +36,7 @@ export const request = hookFetch.create<BaseResponse>({
|
||||
plugins: [
|
||||
sseTextDecoderPlugin({ json: true, prefix: 'data:' }),
|
||||
{
|
||||
name: 'fingerprint-plugin', // 新增指纹插件
|
||||
name: 'fingerprint-plugin',
|
||||
beforeRequest: async (config) => {
|
||||
try {
|
||||
const fingerprint = await getFingerprint();
|
||||
@@ -45,66 +44,110 @@ export const request = hookFetch.create<BaseResponse>({
|
||||
config.headers.set('X-Fingerprint', fingerprint);
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Failed to generate fingerprint:', error);
|
||||
console.error('指纹生成失败:', error);
|
||||
}
|
||||
return config;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'adapt-array-response',
|
||||
afterResponse: async (response) => {
|
||||
if (typeof response.result?.code === 'number') {
|
||||
return response;
|
||||
name: 'jwt-auth',
|
||||
beforeRequest: (config) => {
|
||||
const userStore = useUserStore();
|
||||
if (userStore.token) {
|
||||
config.headers = new Headers(config.headers);
|
||||
config.headers.set('Authorization', `Bearer ${userStore.token}`);
|
||||
}
|
||||
return {
|
||||
...response,
|
||||
result: {
|
||||
code: 200,
|
||||
data: response.result,
|
||||
msg: 'success',
|
||||
},
|
||||
};
|
||||
return config;
|
||||
},
|
||||
afterResponse: async (response: any, config: any) => {
|
||||
const userStore = useUserStore();
|
||||
console.log('网络请求响应----', response);
|
||||
// 成功请求(HTTP状态码200-299)
|
||||
if (response.ok) {
|
||||
// 兼容响应体可能没有code字段的情况
|
||||
const data = await response.json();
|
||||
return {
|
||||
...response,
|
||||
result: data.code ? data : { code: 200, data, msg: 'success' },
|
||||
};
|
||||
}
|
||||
|
||||
// 处理HTTP状态码401
|
||||
if (response.status === 401) {
|
||||
// 刷新Token接口自身失败
|
||||
if (config.url?.includes('/account/refresh')) {
|
||||
userStore.logout();
|
||||
userStore.openLoginDialog();
|
||||
throw new Error('刷新Token失败');
|
||||
}
|
||||
|
||||
// 将请求加入队列
|
||||
if (isRefreshing) {
|
||||
return new Promise((resolve) => {
|
||||
pendingRequests.push(() => resolve(request(config)));
|
||||
});
|
||||
}
|
||||
|
||||
// 开始刷新流程
|
||||
isRefreshing = true;
|
||||
try {
|
||||
const refreshRes = await request.post('/account/refresh', {
|
||||
refresh_token: userStore.refreshToken,
|
||||
}).json();
|
||||
|
||||
// 更新Token
|
||||
userStore.setToken(refreshRes.data.token, refreshRes.data.refreshToken);
|
||||
|
||||
// 重试原始请求
|
||||
config.headers.set('Authorization', `Bearer ${refreshRes.data.token}`);
|
||||
const retryResponse = await request(config);
|
||||
|
||||
// 执行等待中的请求
|
||||
pendingRequests.forEach(cb => cb());
|
||||
pendingRequests = [];
|
||||
return retryResponse;
|
||||
}
|
||||
catch (error) {
|
||||
// 刷新失败清除状态
|
||||
userStore.logout();
|
||||
userStore.openLoginDialog();
|
||||
throw error;
|
||||
}
|
||||
finally {
|
||||
isRefreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理HTTP状态码403
|
||||
if (response.status === 403) {
|
||||
router.replace({ name: '403' });
|
||||
const errorMsg = (await response.json().catch(() => ({})))?.msg || '无权限访问';
|
||||
ElMessage.error(errorMsg);
|
||||
throw new Error('Forbidden');
|
||||
}
|
||||
|
||||
// 其他错误状态码
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
ElMessage.error(errorData.msg || `请求失败: ${response.status}`);
|
||||
throw errorData;
|
||||
},
|
||||
// 错误处理
|
||||
async onError(error: any, config: any) {
|
||||
console.log('网络请求错误----', error);
|
||||
console.log('网络请求错误--config--', config);
|
||||
|
||||
// 可以处理或转换错误
|
||||
if (error.status === 401) {
|
||||
// 处理未授权错误
|
||||
return new Error('Please login first');
|
||||
}
|
||||
return error;
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// JWT插件(保持原有逻辑)
|
||||
function jwtPlugin(): HookFetchPlugin<BaseResponse> {
|
||||
const userStore = useUserStore();
|
||||
return {
|
||||
name: 'jwt',
|
||||
beforeRequest: async (config) => {
|
||||
if (userStore.token) {
|
||||
config.headers = new Headers(config.headers);
|
||||
config.headers.set('authorization', `Bearer ${userStore.token}`);
|
||||
}
|
||||
return config;
|
||||
},
|
||||
afterResponse: async (response) => {
|
||||
if (response.result?.code === 200)
|
||||
return response;
|
||||
|
||||
if (response.result?.code === 403) {
|
||||
router.replace({ name: '403' });
|
||||
ElMessage.error(response.result?.msg);
|
||||
return Promise.reject(response);
|
||||
}
|
||||
|
||||
if (response.result?.code === 401) {
|
||||
userStore.logout();
|
||||
userStore.openLoginDialog();
|
||||
}
|
||||
|
||||
ElMessage.error(response.result?.msg);
|
||||
return Promise.reject(response);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
request.use(jwtPlugin());
|
||||
|
||||
// 导出方法(保持原有)
|
||||
// 保持原有导出
|
||||
export const post = request.post;
|
||||
export const get = request.get;
|
||||
export const put = request.put;
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import useUserStore from "@/stores/user";
|
||||
import router from "@/router";
|
||||
import { Session, Local } from "@/utils/storage";
|
||||
import{computed} from 'vue'
|
||||
import {
|
||||
userLogin,
|
||||
getUserDetailInfo,
|
||||
userLogout,
|
||||
userRegister, userRetrievePassword,
|
||||
} from "@/apis/auth";
|
||||
import {Local, Session} from "@/utils/storage";
|
||||
import {computed} from 'vue'
|
||||
import {getUserDetailInfo, userLogin, userLogout, userRegister, userRetrievePassword,} from "@/apis/auth";
|
||||
|
||||
const TokenKey = "AccessToken";
|
||||
const RefreshTokenKey = "RefreshToken";
|
||||
export const AUTH_MENUS = "AUTH_MENUS";
|
||||
export const AUTH_USER = "AUTH_USER";
|
||||
|
||||
|
||||
export default function useAuths(opt) {
|
||||
|
||||
|
||||
|
||||
|
||||
const defaultOpt = {
|
||||
@@ -35,6 +32,10 @@ export default function useAuths(opt) {
|
||||
var token= Local.get(TokenKey);
|
||||
return token;
|
||||
};
|
||||
// 获取token
|
||||
const getRefreshToken = () => {
|
||||
return Local.get(RefreshTokenKey);
|
||||
};
|
||||
|
||||
const isLogin=computed(()=>{
|
||||
return getToken()? true : false
|
||||
@@ -42,7 +43,7 @@ const isLogin=computed(()=>{
|
||||
|
||||
const currentUserInfo=computed(()=>{
|
||||
return useUserStore();
|
||||
});
|
||||
});
|
||||
|
||||
// 存储token到cookies
|
||||
const setToken = (token) => {
|
||||
@@ -50,7 +51,16 @@ const currentUserInfo=computed(()=>{
|
||||
return false;
|
||||
}
|
||||
Local.set(TokenKey, token);
|
||||
|
||||
|
||||
return true;
|
||||
};
|
||||
// 存储RefreshToken到cookies
|
||||
const setRefreshToken = (token) => {
|
||||
if (token == null) {
|
||||
return false;
|
||||
}
|
||||
Local.set(RefreshTokenKey, token);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -141,9 +151,10 @@ const currentUserInfo=computed(()=>{
|
||||
|
||||
// 登录成功之后的操作
|
||||
const loginSuccess = async (res) => {
|
||||
const { token } = res.data;
|
||||
const { token,refreshToken } = res.data;
|
||||
|
||||
setToken(token);
|
||||
setRefreshToken(refreshToken);
|
||||
useUserStore().updateToken(token);
|
||||
try {
|
||||
// 存储用户信息
|
||||
@@ -195,10 +206,12 @@ const currentUserInfo=computed(()=>{
|
||||
// console.log(error);
|
||||
// }
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
getToken,
|
||||
getRefreshToken,
|
||||
setToken,
|
||||
setRefreshToken,
|
||||
removeToken,
|
||||
loginFun,
|
||||
getUserInfo,
|
||||
|
||||
@@ -6,12 +6,13 @@ import "nprogress/nprogress.css";
|
||||
import useUserStore from "@/stores/user";
|
||||
|
||||
NProgress.configure({ showSpinner: false });
|
||||
const { getToken, logoutFun } = useAuths();
|
||||
const { getToken, logoutFun ,getRefreshToken} = useAuths();
|
||||
const whiteList = ["/login", "/auth-redirect", "/bind", "/register"];
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
NProgress.start();
|
||||
const hasToken = getToken();
|
||||
const refreshToken = getRefreshToken();
|
||||
if (to.path === "/login" || to.path === "/index") {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const isPopup = window.opener && window.opener !== window;
|
||||
@@ -24,7 +25,8 @@ router.beforeEach((to, from, next) => {
|
||||
const targetOrigin = new URL(decodeURIComponent(redirectUri)).origin;
|
||||
window.opener.postMessage({
|
||||
type: 'SSO_LOGIN_SUCCESS',
|
||||
token: hasToken
|
||||
token: hasToken,
|
||||
refreshToken: refreshToken,
|
||||
}, targetOrigin);
|
||||
// 立即关闭窗口
|
||||
setTimeout(() => window.close(), 100);
|
||||
|
||||
Reference in New Issue
Block a user