feat(token): 添加token类型判断和过期处理逻辑
添加单/双token类型判断函数 实现token过期时间计算和存储 增加token有效性检查和自动刷新功能 完善登录/登出/刷新token的错误处理
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import type { IAuthLoginRes, IDoubleTokenRes, ISingleTokenRes } from '@/api/types/login'
|
||||
import type { IAuthLoginRes } from '@/api/types/login'
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { computed, ref } from 'vue' // 修复:导入 computed
|
||||
import {
|
||||
login as _login,
|
||||
logout as _logout,
|
||||
@@ -8,9 +8,13 @@ import {
|
||||
wxLogin as _wxLogin,
|
||||
getWxCode,
|
||||
} from '@/api/login'
|
||||
import { isDoubleTokenRes, isSingleTokenRes } from '@/api/types/login'
|
||||
import { isDoubleTokenMode } from '@/utils'
|
||||
import { useUserStore } from './user'
|
||||
|
||||
// 修复:添加 isSingleTokenMode 变量
|
||||
export const isSingleTokenMode = !isDoubleTokenMode
|
||||
|
||||
// 初始化状态
|
||||
const tokenInfoState = isDoubleTokenMode
|
||||
? {
|
||||
@@ -32,8 +36,54 @@ export const useTokenStore = defineStore(
|
||||
// 设置用户信息
|
||||
const setTokenInfo = (val: IAuthLoginRes) => {
|
||||
tokenInfo.value = val
|
||||
|
||||
// 计算并存储过期时间
|
||||
const now = Date.now()
|
||||
if (isSingleTokenRes(val)) {
|
||||
// 单token模式
|
||||
const expireTime = now + val.expiresIn * 1000
|
||||
uni.setStorageSync('accessTokenExpireTime', expireTime)
|
||||
}
|
||||
else if (isDoubleTokenRes(val)) {
|
||||
// 双token模式
|
||||
const accessExpireTime = now + val.accessExpiresIn * 1000
|
||||
const refreshExpireTime = now + val.refreshExpiresIn * 1000
|
||||
uni.setStorageSync('accessTokenExpireTime', accessExpireTime)
|
||||
uni.setStorageSync('refreshTokenExpireTime', refreshExpireTime)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断token是否过期
|
||||
*/
|
||||
const isTokenExpired = computed(() => {
|
||||
const now = Date.now()
|
||||
const expireTime = uni.getStorageSync('accessTokenExpireTime')
|
||||
|
||||
if (!expireTime)
|
||||
return true
|
||||
return now >= expireTime
|
||||
})
|
||||
|
||||
/**
|
||||
* 判断refreshToken是否过期
|
||||
*/
|
||||
const isRefreshTokenExpired = computed(() => {
|
||||
if (!isDoubleTokenMode)
|
||||
return true
|
||||
|
||||
const now = Date.now()
|
||||
const refreshExpireTime = uni.getStorageSync('refreshTokenExpireTime')
|
||||
|
||||
if (!refreshExpireTime)
|
||||
return true
|
||||
return now >= refreshExpireTime
|
||||
})
|
||||
|
||||
/**
|
||||
* 登录成功后处理逻辑
|
||||
* @param tokenInfo 登录返回的token信息
|
||||
*/
|
||||
async function _postLogin(tokenInfo: IAuthLoginRes) {
|
||||
setTokenInfo(tokenInfo)
|
||||
const userStore = useUserStore()
|
||||
@@ -43,7 +93,7 @@ export const useTokenStore = defineStore(
|
||||
/**
|
||||
* 用户登录
|
||||
* @param credentials 登录参数
|
||||
* @returns R<IUserLogin>
|
||||
* @returns 登录结果
|
||||
*/
|
||||
const login = async (credentials: {
|
||||
username: string
|
||||
@@ -51,62 +101,178 @@ export const useTokenStore = defineStore(
|
||||
code: string
|
||||
uuid: string
|
||||
}) => {
|
||||
const res = await _login(credentials)
|
||||
console.log('普通登录-res: ', res)
|
||||
await _postLogin(res.data)
|
||||
uni.showToast({
|
||||
title: '登录成功',
|
||||
icon: 'success',
|
||||
})
|
||||
try {
|
||||
const res = await _login(credentials)
|
||||
console.log('普通登录-res: ', res)
|
||||
await _postLogin(res.data)
|
||||
uni.showToast({
|
||||
title: '登录成功',
|
||||
icon: 'success',
|
||||
})
|
||||
return res
|
||||
}
|
||||
catch (error) {
|
||||
console.error('登录失败:', error)
|
||||
uni.showToast({
|
||||
title: '登录失败,请重试',
|
||||
icon: 'error',
|
||||
})
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信登录
|
||||
* @returns 登录结果
|
||||
*/
|
||||
const wxLogin = async () => {
|
||||
// 获取微信小程序登录的code
|
||||
const code = await getWxCode()
|
||||
console.log('微信登录-code: ', code)
|
||||
const res = await _wxLogin(code)
|
||||
console.log('微信登录-res: ', res)
|
||||
await _postLogin(res.data)
|
||||
uni.showToast({
|
||||
title: '登录成功',
|
||||
icon: 'success',
|
||||
})
|
||||
try {
|
||||
// 获取微信小程序登录的code
|
||||
const code = await getWxCode()
|
||||
console.log('微信登录-code: ', code)
|
||||
const res = await _wxLogin(code)
|
||||
console.log('微信登录-res: ', res)
|
||||
await _postLogin(res.data)
|
||||
uni.showToast({
|
||||
title: '登录成功',
|
||||
icon: 'success',
|
||||
})
|
||||
return res
|
||||
}
|
||||
catch (error) {
|
||||
console.error('微信登录失败:', error)
|
||||
uni.showToast({
|
||||
title: '微信登录失败,请重试',
|
||||
icon: 'error',
|
||||
})
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出登录 并 删除用户信息
|
||||
*/
|
||||
const logout = async () => {
|
||||
_logout()
|
||||
const userStore = useUserStore()
|
||||
await userStore.removeUserInfo()
|
||||
try {
|
||||
await _logout()
|
||||
|
||||
// 清除存储的过期时间
|
||||
uni.removeStorageSync('accessTokenExpireTime')
|
||||
uni.removeStorageSync('refreshTokenExpireTime')
|
||||
}
|
||||
catch (error) {
|
||||
console.error('退出登录失败:', error)
|
||||
}
|
||||
finally {
|
||||
// 无论成功失败,都需要清除本地token信息
|
||||
const userStore = useUserStore()
|
||||
await userStore.removeUserInfo()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新token
|
||||
* @returns 刷新结果
|
||||
*/
|
||||
const refreshToken = async () => {
|
||||
const refreshToken = (tokenInfo.value as IDoubleTokenRes).refreshToken
|
||||
const res = await _refreshToken(refreshToken)
|
||||
console.log('刷新token-res: ', res)
|
||||
setTokenInfo(res.data)
|
||||
if (!isDoubleTokenMode) {
|
||||
console.error('单token模式不支持刷新token')
|
||||
throw new Error('单token模式不支持刷新token')
|
||||
}
|
||||
|
||||
try {
|
||||
// 安全检查,确保refreshToken存在
|
||||
if (!isDoubleTokenRes(tokenInfo.value) || !tokenInfo.value.refreshToken) {
|
||||
throw new Error('无效的refreshToken')
|
||||
}
|
||||
|
||||
const refreshToken = tokenInfo.value.refreshToken
|
||||
const res = await _refreshToken(refreshToken)
|
||||
console.log('刷新token-res: ', res)
|
||||
setTokenInfo(res.data)
|
||||
return res
|
||||
}
|
||||
catch (error) {
|
||||
console.error('刷新token失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const hasLogin = computed(() => isDoubleTokenMode
|
||||
? (tokenInfo.value as IDoubleTokenRes).accessToken
|
||||
: (tokenInfo.value as ISingleTokenRes).token,
|
||||
)
|
||||
/**
|
||||
* 获取有效的token
|
||||
* 注意:在computed中不直接调用异步函数,只做状态判断
|
||||
* 实际的刷新操作应由调用方处理
|
||||
*/
|
||||
const getValidToken = computed(() => {
|
||||
// token已过期,返回空
|
||||
if (isTokenExpired.value) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (isSingleTokenMode) {
|
||||
return isSingleTokenRes(tokenInfo.value) ? tokenInfo.value.token : ''
|
||||
}
|
||||
else {
|
||||
return isDoubleTokenRes(tokenInfo.value) ? tokenInfo.value.accessToken : ''
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 检查是否有登录信息(不考虑token是否过期)
|
||||
*/
|
||||
const hasLoginInfo = computed(() => {
|
||||
if (isDoubleTokenMode) {
|
||||
return isDoubleTokenRes(tokenInfo.value) && !!tokenInfo.value.accessToken
|
||||
}
|
||||
else {
|
||||
return isSingleTokenRes(tokenInfo.value) && !!tokenInfo.value.token
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 检查是否已登录且token有效
|
||||
*/
|
||||
const hasValidLogin = computed(() => {
|
||||
return hasLoginInfo.value && !isTokenExpired.value
|
||||
})
|
||||
|
||||
/**
|
||||
* 尝试获取有效的token,如果过期且可刷新,则刷新token
|
||||
* @returns 有效的token或空字符串
|
||||
*/
|
||||
const tryGetValidToken = async (): Promise<string> => {
|
||||
if (!getValidToken.value && isDoubleTokenMode && !isRefreshTokenExpired.value) {
|
||||
try {
|
||||
await refreshToken()
|
||||
return getValidToken.value
|
||||
}
|
||||
catch (error) {
|
||||
console.error('尝试刷新token失败:', error)
|
||||
return ''
|
||||
}
|
||||
}
|
||||
return getValidToken.value
|
||||
}
|
||||
|
||||
return {
|
||||
tokenInfo,
|
||||
hasLogin,
|
||||
// 核心API方法
|
||||
login,
|
||||
wxLogin,
|
||||
logout,
|
||||
|
||||
// 认证状态判断(最常用的)
|
||||
hasLogin: hasValidLogin,
|
||||
|
||||
// 内部系统使用的方法
|
||||
refreshToken,
|
||||
tryGetValidToken,
|
||||
|
||||
// 调试或特殊场景可能需要直接访问的信息
|
||||
tokenInfo,
|
||||
}
|
||||
},
|
||||
{
|
||||
// 添加持久化配置,确保刷新页面后token信息不丢失
|
||||
persist: true,
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user