| | |
| | | return !!(token && userInfo) |
| | | } |
| | | |
| | | /** |
| | | * 解析并判断JWT是否过期 |
| | | * 规则:若token为空或无法解析exp,视为过期;exp单位为秒 |
| | | */ |
| | | export function isTokenExpired(token?: string | null): boolean { |
| | | if (!token) return true |
| | | try { |
| | | const parts = token.split('.') |
| | | if (parts.length !== 3) return true |
| | | const payloadJson = JSON.parse(decodeBase64Url(parts[1])) |
| | | const exp = payloadJson?.exp |
| | | if (!exp || typeof exp !== 'number') return true |
| | | const now = Math.floor(Date.now() / 1000) |
| | | return exp <= now |
| | | } catch (e) { |
| | | console.error('解析JWT失败:', e) |
| | | return true |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Base64Url 解码 |
| | | */ |
| | | function decodeBase64Url(input: string): string { |
| | | // 替换URL安全字符并补齐'=' |
| | | let base64 = input.replace(/-/g, '+').replace(/_/g, '/') |
| | | const pad = base64.length % 4 |
| | | if (pad) { |
| | | base64 += '='.repeat(4 - pad) |
| | | } |
| | | const decoded = atob(base64) |
| | | // 处理UTF-8 |
| | | try { |
| | | return decodeURIComponent( |
| | | decoded |
| | | .split('') |
| | | .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)) |
| | | .join('') |
| | | ) |
| | | } catch { |
| | | return decoded |
| | | } |
| | | } |
| | | |
| | | // 清除所有认证数据 |
| | | export function clearAuth(): void { |
| | | removeToken() |