151 lines
3.6 KiB
JavaScript
151 lines
3.6 KiB
JavaScript
const jwt = require('jsonwebtoken');
|
||
const logger = require('./logger');
|
||
|
||
/**
|
||
* JWT 工具类
|
||
* 提供 token 生成、验证、刷新等功能
|
||
*/
|
||
class JWTUtils {
|
||
constructor() {
|
||
this.secret = process.env.JWT_SECRET || 'your-secret-key';
|
||
this.refreshSecret = process.env.JWT_REFRESH_SECRET || 'your-refresh-secret-key';
|
||
// Access token 过期时间(默认 2 小时)
|
||
this.accessTokenExpiry = process.env.JWT_ACCESS_EXPIRY || '24h';
|
||
// Refresh token 过期时间(默认 7 天)
|
||
this.refreshTokenExpiry = process.env.JWT_REFRESH_EXPIRY || '7d';
|
||
}
|
||
|
||
/**
|
||
* 生成访问令牌
|
||
* @param {Object} payload - 用户信息载荷
|
||
* @returns {String} access token
|
||
*/
|
||
generateAccessToken(payload) {
|
||
const data = {
|
||
userId: payload.userId,
|
||
username: payload.username,
|
||
corpId: payload.corpId,
|
||
role: payload.role,
|
||
type: 'access',
|
||
platform: payload.platform || '',
|
||
};
|
||
|
||
return jwt.sign(data, this.secret, {
|
||
expiresIn: this.accessTokenExpiry,
|
||
issuer: 'zyt-hlw-service'
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 生成刷新令牌
|
||
* @param {Object} payload - 用户信息载荷
|
||
* @returns {String} refresh token
|
||
*/
|
||
generateRefreshToken(payload) {
|
||
const data = {
|
||
userId: payload.userId,
|
||
username: payload.username,
|
||
type: 'refresh'
|
||
};
|
||
|
||
return jwt.sign(data, this.refreshSecret, {
|
||
expiresIn: this.refreshTokenExpiry,
|
||
issuer: 'zyt-hlw-service'
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 生成访问令牌和刷新令牌
|
||
* @param {Object} payload - 用户信息载荷
|
||
* @returns {Object} { accessToken, refreshToken }
|
||
*/
|
||
generateTokens(payload) {
|
||
const accessToken = this.generateAccessToken(payload);
|
||
const refreshToken = this.generateRefreshToken(payload);
|
||
|
||
return {
|
||
accessToken,
|
||
refreshToken,
|
||
expiresIn: this.parseExpiry(this.accessTokenExpiry)
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 验证访问令牌
|
||
* @param {String} token - 访问令牌
|
||
* @returns {Object} 解码后的载荷
|
||
*/
|
||
verifyAccessToken(token) {
|
||
return jwt.verify(token, this.secret);
|
||
}
|
||
|
||
/**
|
||
* 验证刷新令牌
|
||
* @param {String} token - 刷新令牌
|
||
* @returns {Object} 解码后的载荷
|
||
*/
|
||
verifyRefreshToken(token) {
|
||
return jwt.verify(token, this.refreshSecret);
|
||
}
|
||
|
||
/**
|
||
* 使用刷新令牌生成新的访问令牌
|
||
* @param {String} refreshToken - 刷新令牌
|
||
* @returns {Object} { accessToken, expiresIn }
|
||
*/
|
||
refreshAccessToken(refreshToken) {
|
||
const decoded = this.verifyRefreshToken(refreshToken);
|
||
|
||
if (decoded.type !== 'refresh') {
|
||
throw new Error('Invalid token type');
|
||
}
|
||
|
||
// 生成新的访问令牌
|
||
const accessToken = this.generateAccessToken({
|
||
userId: decoded.userId,
|
||
username: decoded.username
|
||
});
|
||
|
||
return {
|
||
accessToken,
|
||
expiresIn: this.parseExpiry(this.accessTokenExpiry)
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 解析过期时间字符串为秒数
|
||
* @param {String} expiry - 过期时间字符串(如 '2h', '7d')
|
||
* @returns {Number} 秒数
|
||
*/
|
||
parseExpiry(expiry) {
|
||
const match = expiry.match(/^(\d+)([smhd])$/);
|
||
if (!match) return 7200; // 默认 2 小时
|
||
|
||
const value = parseInt(match[1]);
|
||
const unit = match[2];
|
||
|
||
const multipliers = {
|
||
's': 1,
|
||
'm': 60,
|
||
'h': 3600,
|
||
'd': 86400
|
||
};
|
||
|
||
return value * (multipliers[unit] || 3600);
|
||
}
|
||
|
||
/**
|
||
* 解码 token(不验证签名)
|
||
* @param {String} token
|
||
* @returns {Object} 解码后的载荷
|
||
*/
|
||
decode(token) {
|
||
return jwt.decode(token);
|
||
}
|
||
}
|
||
|
||
// 导出单例
|
||
module.exports = new JWTUtils();
|
||
|
||
|