hookehuyr

refactor(auth): 重构用户信息存储逻辑,添加缓存和工具函数

将直接操作 localStorage 的用户信息逻辑封装到工具函数中
添加缓存机制减少 localStorage 读取次数
提供统一的用户信息获取、设置和删除方法
......@@ -11,6 +11,7 @@ import { ref, provide, inject, onMounted } from 'vue'
import { logoutAPI, getUserInfoAPI } from '@/api/users'
import { getAuthInfoAPI } from '@/api/auth'
import { clearAuthHeaders, setAuthHeaders } from '@/utils/axios'
import { applyUserInfoAuth, getUserInfoFromStorage, removeUserInfoFromStorage } from '@/utils/auth_user_info'
// 创建认证上下文的Symbol key,用于provide/inject依赖注入
// 使用Symbol确保key的唯一性,避免命名冲突
......@@ -68,8 +69,8 @@ export function provideAuth() {
currentUser.value = { ...data.user, ...data.checkin }
localStorage.setItem('currentUser', JSON.stringify(currentUser.value))
// 重新设置认证头
const userInfo = JSON.parse(localStorage.getItem('user_info') || '{}')
if (userInfo.user_id && userInfo.HTTP_USER_TOKEN) {
const userInfo = getUserInfoFromStorage()
if (userInfo && userInfo.user_id && userInfo.HTTP_USER_TOKEN) {
setAuthHeaders(userInfo.user_id, userInfo.HTTP_USER_TOKEN)
}
} else {
......@@ -80,14 +81,14 @@ export function provideAuth() {
if(code) {
// 如果接口返回了 user_info,先保存
if (data.user_info) {
localStorage.setItem('user_info', JSON.stringify(data.user_info))
applyUserInfoAuth(data.user_info, { set_auth_headers: setAuthHeaders })
}
// 查询用户是否授权或已登录
if (data.openid_has || data?.user?.id) {
// 重新设置认证头
const userInfo = JSON.parse(localStorage.getItem('user_info') || '{}')
if (userInfo.user_id && userInfo.HTTP_USER_TOKEN) {
const userInfo = getUserInfoFromStorage()
if (userInfo && userInfo.user_id && userInfo.HTTP_USER_TOKEN) {
setAuthHeaders(userInfo.user_id, userInfo.HTTP_USER_TOKEN)
}
......@@ -144,7 +145,7 @@ export function provideAuth() {
currentUser.value = null
// 清理本地存储的用户信息和登录时间戳
localStorage.removeItem('currentUser')
localStorage.removeItem('user_info')
removeUserInfoFromStorage()
// localStorage.removeItem('loginTimestamp')
// 清除认证请求头
clearAuthHeaders()
......@@ -154,7 +155,7 @@ export function provideAuth() {
// 即使API调用失败,也要清理本地状态
currentUser.value = null
localStorage.removeItem('currentUser')
localStorage.removeItem('user_info')
removeUserInfoFromStorage()
clearAuthHeaders()
}
}
......
......@@ -25,5 +25,97 @@ export const applyUserInfoAuth = (input, options = {}) => {
storage.setItem('user_info', JSON.stringify(user_info || {}))
}
if (storage === globalThis.localStorage) {
cached_user_info_str = JSON.stringify(user_info || {})
cached_user_info = user_info || {}
}
return true
}
let cached_user_info_str = null
let cached_user_info = null
const safeParse = (text) => {
try {
return JSON.parse(text)
} catch (e) {
return null
}
}
/**
* 从存储中获取用户信息
* @param {*} options 选项对象,包含 storage 字段
* @returns 如果用户信息存在且有效,则返回用户信息对象;否则返回 null
*/
export const getUserInfoFromStorage = (options = {}) => {
const storage = Object.prototype.hasOwnProperty.call(options, 'storage') ? options.storage : globalThis.localStorage
if (!storage || typeof storage.getItem !== 'function') {
return null
}
const raw = storage.getItem('user_info')
if (!raw) {
if (storage === globalThis.localStorage) {
cached_user_info_str = null
cached_user_info = null
}
return null
}
if (storage === globalThis.localStorage && cached_user_info_str === raw && cached_user_info) {
return cached_user_info
}
const parsed = safeParse(raw)
if (!parsed || typeof parsed !== 'object') {
if (storage === globalThis.localStorage) {
cached_user_info_str = null
cached_user_info = null
}
return null
}
if (storage === globalThis.localStorage) {
cached_user_info_str = raw
cached_user_info = parsed
}
return parsed
}
/**
* 从存储中移除用户信息
* @param {*} options 选项对象,包含 storage 字段
*/
export const removeUserInfoFromStorage = (options = {}) => {
const storage = Object.prototype.hasOwnProperty.call(options, 'storage') ? options.storage : globalThis.localStorage
if (storage && typeof storage.removeItem === 'function') {
storage.removeItem('user_info')
}
if (storage === globalThis.localStorage) {
cached_user_info_str = null
cached_user_info = null
}
}
/**
* 从用户信息中提取认证头信息
* @param {*} user_info 用户信息对象,包含 user_id 和 HTTP_USER_TOKEN 字段
* @returns 如果用户信息有效,则返回包含 User-Id 和 User-Token 字段的对象;否则返回空对象
*/
export const getAuthHeadersFromUserInfo = (user_info) => {
if (!user_info || typeof user_info !== 'object') {
return {}
}
const user_id = user_info.user_id
const user_token = user_info.HTTP_USER_TOKEN
if (!user_id || !user_token) {
return {}
}
return {
'User-Id': user_id,
'User-Token': user_token
}
}
......
......@@ -9,6 +9,7 @@
import axios from 'axios';
import router from '@/router';
import { checkAuth } from '@/router/guards'
import { getUserInfoFromStorage, removeUserInfoFromStorage } from '@/utils/auth_user_info'
// import qs from 'Qs'
// import { strExist } from '@/utils/tools'
......@@ -47,10 +48,10 @@ axios.interceptors.request.use(
* 动态获取 user_info 并设置到请求头
* 确保每个请求都带上最新的 user_info
*/
const user_info = localStorage.getItem('user_info') ? JSON.parse(localStorage.getItem('user_info')) : {};
const user_info = getUserInfoFromStorage()
if (user_info) {
config.headers['User-Id'] = user_info.user_id;
config.headers['User-Token'] = user_info.HTTP_USER_TOKEN;
config.headers['User-Id'] = user_info.user_id
config.headers['User-Token'] = user_info.HTTP_USER_TOKEN
}
// const url_params = parseQueryString(location.href);
......@@ -88,6 +89,7 @@ axios.interceptors.response.use(
if (need_login_redirect) {
// 仅在受限页面触发登录重定向
localStorage.removeItem('currentUser');
removeUserInfoFromStorage()
clearAuthHeaders();
const current_path = to.fullPath;
router.push(`/login?redirect=${encodeURIComponent(current_path)}`);
......