websocket优化

This commit is contained in:
2025-08-06 13:33:09 +08:00
parent 83998f88ac
commit d6d63523a3
3 changed files with 879 additions and 138 deletions

View File

@@ -0,0 +1,234 @@
import { useUserStore } from "@/stores/modules/user";
// JWT Token解析后的载荷接口
export interface JwtPayload {
userId?: string;
loginName?: string;
exp?: number;
iat?: number;
[key: string]: any;
}
// Token信息摘要接口
export interface TokenInfo {
userId: string | null;
loginName: string | null;
expiration: string | null;
isExpired: boolean;
remainingTime: string;
}
/**
* JWT工具类
* 提供JWT token的解析、验证等功能
*/
export class JwtUtil {
/**
* Base64URL解码
* @param str Base64URL编码的字符串
*/
private static base64UrlDecode(str: string): string {
try {
// Base64URL转Base64
let base64 = str.replace(/-/g, '+').replace(/_/g, '/');
// 补齐padding
while (base64.length % 4) {
base64 += '=';
}
// Base64解码
const decoded = atob(base64);
// 处理UTF-8编码
return decodeURIComponent(
decoded
.split('')
.map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
.join('')
);
} catch (error) {
throw new Error('Base64URL解码失败: ' + error);
}
}
/**
* 解析JWT Token获取载荷信息
* @param token JWT token字符串如果不传则从store中获取
*/
static parseToken(token?: string): JwtPayload | null {
try {
let targetToken = token;
// 如果没有传入token从store中获取
if (!targetToken) {
const userStore = useUserStore();
targetToken = userStore.accessToken;
}
if (!targetToken) {
console.warn('Token不存在');
return null;
}
// JWT token由三部分组成用.分割header.payload.signature
const parts = targetToken.split('.');
if (parts.length !== 3) {
console.error('无效的JWT token格式');
return null;
}
// 解码payload部分第二部分
const payload = parts[1];
const decodedPayload = this.base64UrlDecode(payload);
const tokenData: JwtPayload = JSON.parse(decodedPayload);
return tokenData;
} catch (error) {
console.error('解析JWT Token失败:', error);
return null;
}
}
/**
* 获取指定字段的值
* @param field 字段名
* @param token JWT token字符串可选
*/
static getField<T = any>(field: string, token?: string): T | null {
const tokenData = this.parseToken(token);
return tokenData?.[field] || null;
}
/**
* 获取用户ID
* @param token JWT token字符串可选
*/
static getUserId(token?: string): string | null {
return this.getField<string>('userId', token);
}
/**
* 获取登录名
* @param token JWT token字符串可选
*/
static getLoginName(token?: string): string | null {
return this.getField<string>('loginName', token);
}
/**
* 获取Token过期时间戳
* @param token JWT token字符串可选
*/
static getExpiration(token?: string): number | null {
return this.getField<number>('exp', token);
}
/**
* 获取Token签发时间戳
* @param token JWT token字符串可选
*/
static getIssuedAt(token?: string): number | null {
return this.getField<number>('iat', token);
}
/**
* 检查Token是否过期
* @param token JWT token字符串可选
*/
static isExpired(token?: string): boolean {
const exp = this.getExpiration(token);
if (!exp) return true;
// JWT中的exp是秒级时间戳需要转换为毫秒
const expTime = exp * 1000;
return Date.now() >= expTime;
}
/**
* 获取Token剩余有效时间毫秒
* @param token JWT token字符串可选
*/
static getRemainingTime(token?: string): number {
const exp = this.getExpiration(token);
if (!exp) return 0;
const expTime = exp * 1000;
const remaining = expTime - Date.now();
return Math.max(0, remaining);
}
/**
* 格式化剩余时间为可读字符串
* @param ms 毫秒数
*/
static formatRemainingTime(ms: number): string {
if (ms <= 0) return '已过期';
const seconds = Math.floor(ms / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
if (days > 0) return `${days}${hours % 24}小时`;
if (hours > 0) return `${hours}小时 ${minutes % 60}分钟`;
if (minutes > 0) return `${minutes}分钟 ${seconds % 60}`;
return `${seconds}`;
}
/**
* 获取完整的Token信息摘要
* @param token JWT token字符串可选
*/
static getTokenInfo(token?: string): TokenInfo {
const exp = this.getExpiration(token);
const remainingMs = this.getRemainingTime(token);
return {
userId: this.getUserId(token),
loginName: this.getLoginName(token),
expiration: exp ? new Date(exp * 1000).toLocaleString() : null,
isExpired: this.isExpired(token),
remainingTime: this.formatRemainingTime(remainingMs)
};
}
/**
* 验证Token是否有效格式正确且未过期
* @param token JWT token字符串可选
*/
static isValid(token?: string): boolean {
const tokenData = this.parseToken(token);
return tokenData !== null && !this.isExpired(token);
}
/**
* 获取Token的头部信息
* @param token JWT token字符串可选
*/
static getHeader(token?: string): any | null {
try {
let targetToken = token;
if (!targetToken) {
const userStore = useUserStore();
targetToken = userStore.accessToken;
}
if (!targetToken) return null;
const parts = targetToken.split('.');
if (parts.length !== 3) return null;
const header = parts[0];
const decodedHeader = this.base64UrlDecode(header);
return JSON.parse(decodedHeader);
} catch (error) {
console.error('解析JWT Header失败:', error);
return null;
}
}
}
// 导出单例方法,方便直接调用
export const jwtUtil = JwtUtil;