websocket优化
This commit is contained in:
234
frontend/src/utils/jwtUtil.ts
Normal file
234
frontend/src/utils/jwtUtil.ts
Normal 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;
|
||||
Reference in New Issue
Block a user