feat: 功能
This commit is contained in:
215
frontend/utils/token-manager.js
Normal file
215
frontend/utils/token-manager.js
Normal file
@@ -0,0 +1,215 @@
|
||||
import { useCache, CACHE_KEY, deleteTokenCache } from '../hooks/web/useCache'
|
||||
|
||||
/**
|
||||
* Token 统一管理模块(Monorepo 全局工具)
|
||||
*
|
||||
* 这是项目中唯一的 token 管理入口,所有 token 操作都应该通过此模块进行。
|
||||
*
|
||||
* Token 存储优先级(读取顺序):
|
||||
* 1. 手动输入的 dev token (sessionStorage)
|
||||
* 2. 正式登录的 token (wsCache/localStorage)
|
||||
* 3. 环境变量 VITE_DEV_TOKEN
|
||||
*
|
||||
* Token 存储位置:
|
||||
* - ACCESS_TOKEN/access_token: 访问令牌(wsCache/localStorage)
|
||||
* - REFRESH_TOKEN/refresh_token: 刷新令牌(wsCache/localStorage)
|
||||
* - DEV_MANUAL_TOKEN: 开发手动输入的token(sessionStorage)
|
||||
*/
|
||||
|
||||
// ==================== 常量定义 ====================
|
||||
const DEV_MANUAL_TOKEN_KEY = 'DEV_MANUAL_TOKEN'
|
||||
|
||||
// Token 键名变体(支持大小写不同)
|
||||
const TOKEN_KEYS = {
|
||||
ACCESS: [CACHE_KEY.ACCESS_TOKEN, 'access_token'],
|
||||
REFRESH: [CACHE_KEY.REFRESH_TOKEN, 'refresh_token']
|
||||
}
|
||||
|
||||
// ==================== 缓存实例管理 ====================
|
||||
let wsCache = null
|
||||
|
||||
/**
|
||||
* 获取 wsCache 实例(延迟初始化,避免模块加载时出错)
|
||||
* @returns {Object} wsCache 实例
|
||||
*/
|
||||
function getCache() {
|
||||
if (!wsCache) {
|
||||
try {
|
||||
wsCache = useCache().wsCache
|
||||
} catch (e) {
|
||||
console.warn('初始化 wsCache 失败:', e)
|
||||
// 返回一个安全的空对象,避免后续调用出错
|
||||
wsCache = {
|
||||
get: () => null,
|
||||
set: () => {},
|
||||
delete: () => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
return wsCache
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取完整的 Authorization Header 值
|
||||
* @returns {string} Bearer token 或空字符串
|
||||
*/
|
||||
export function getAuthHeader() {
|
||||
const token = getToken()
|
||||
return token ? `Bearer ${token}` : ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 token
|
||||
* @returns {string} token 字符串
|
||||
*/
|
||||
export function getToken() {
|
||||
// 1. 优先使用手动输入的 dev token
|
||||
const manualToken = sessionStorage.getItem(DEV_MANUAL_TOKEN_KEY)
|
||||
if (manualToken) {
|
||||
return manualToken
|
||||
}
|
||||
|
||||
// 2. 使用正式登录的 token(从 wsCache 读取)
|
||||
try {
|
||||
const cache = getCache()
|
||||
// 尝试所有可能的键名变体
|
||||
for (const key of TOKEN_KEYS.ACCESS) {
|
||||
const accessToken = cache.get(key)
|
||||
if (accessToken) {
|
||||
return accessToken
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('获取 wsCache 失败:', e)
|
||||
}
|
||||
|
||||
// 3. 兜底:环境变量中的 token
|
||||
const envToken = import.meta?.env?.VITE_DEV_TOKEN
|
||||
if (envToken) {
|
||||
return envToken
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置手动输入的 dev token
|
||||
* @param {string} token
|
||||
*/
|
||||
export function setDevToken(token) {
|
||||
if (token) {
|
||||
sessionStorage.setItem(DEV_MANUAL_TOKEN_KEY, token)
|
||||
} else {
|
||||
sessionStorage.removeItem(DEV_MANUAL_TOKEN_KEY)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取手动输入的 dev token(用于显示)
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getDevToken() {
|
||||
return sessionStorage.getItem(DEV_MANUAL_TOKEN_KEY) || ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置访问令牌和刷新令牌
|
||||
* @param {Object} tokens - token 对象
|
||||
* @param {string} tokens.accessToken - 访问令牌
|
||||
* @param {string} tokens.refreshToken - 刷新令牌
|
||||
*/
|
||||
export function setToken(tokens) {
|
||||
if (!tokens || (!tokens.accessToken && !tokens.refreshToken)) {
|
||||
console.warn('setToken: token 参数无效', tokens)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const cache = getCache()
|
||||
if (tokens.accessToken) {
|
||||
cache.set(CACHE_KEY.ACCESS_TOKEN, tokens.accessToken)
|
||||
}
|
||||
if (tokens.refreshToken) {
|
||||
cache.set(CACHE_KEY.REFRESH_TOKEN, tokens.refreshToken)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('设置 token 失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取刷新令牌
|
||||
* @returns {string|null} 刷新令牌或 null
|
||||
*/
|
||||
export function getRefreshToken() {
|
||||
try {
|
||||
const cache = getCache()
|
||||
// 尝试所有可能的键名变体
|
||||
for (const key of TOKEN_KEYS.REFRESH) {
|
||||
const token = cache.get(key)
|
||||
if (token) {
|
||||
return token
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('获取 refresh token 失败:', e)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有 token
|
||||
* 清空所有可能的 token 存储位置,确保完全清除
|
||||
*/
|
||||
export function clearAllTokens() {
|
||||
try {
|
||||
// 1. 清空 sessionStorage 中的 dev token
|
||||
sessionStorage.removeItem(DEV_MANUAL_TOKEN_KEY)
|
||||
} catch (e) {
|
||||
console.warn('清除 sessionStorage token 失败:', e)
|
||||
}
|
||||
|
||||
try {
|
||||
// 2. 清空 wsCache (localStorage) 中的所有可能的 token 键名
|
||||
// 使用统一的 deleteTokenCache 函数,保持与 useCache 的一致性
|
||||
deleteTokenCache()
|
||||
} catch (e) {
|
||||
console.warn('清除 wsCache token 失败:', e)
|
||||
}
|
||||
|
||||
try {
|
||||
// 3. 清空 localStorage 中可能的 token(兜底处理)
|
||||
// 注意:wsCache 默认使用 localStorage,但为了确保清除,也直接操作 localStorage
|
||||
const localStorageKeys = [
|
||||
...TOKEN_KEYS.ACCESS,
|
||||
...TOKEN_KEYS.REFRESH,
|
||||
DEV_MANUAL_TOKEN_KEY
|
||||
]
|
||||
localStorageKeys.forEach(key => {
|
||||
try {
|
||||
localStorage.removeItem(key)
|
||||
} catch (e) {
|
||||
// 忽略单个键删除失败
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.warn('清除 localStorage token 失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 token(别名,兼容旧代码)
|
||||
* @deprecated 使用 clearAllTokens() 代替
|
||||
*/
|
||||
export function removeToken() {
|
||||
clearAllTokens()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取访问令牌(别名,兼容旧代码)
|
||||
* @returns {string} token 字符串
|
||||
*/
|
||||
export function getAccessToken() {
|
||||
return getToken()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user