befly 3.8.29 → 3.8.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -6
- package/checks/checkApi.ts +2 -1
- package/checks/checkApp.ts +31 -1
- package/checks/checkTable.ts +3 -2
- package/hooks/cors.ts +3 -3
- package/hooks/parser.ts +8 -6
- package/hooks/permission.ts +12 -5
- package/hooks/validator.ts +1 -1
- package/lib/cacheHelper.ts +73 -65
- package/lib/cipher.ts +2 -1
- package/lib/connect.ts +23 -52
- package/lib/dbHelper.ts +14 -11
- package/lib/jwt.ts +58 -437
- package/lib/logger.ts +76 -197
- package/lib/redisHelper.ts +163 -1
- package/lib/sqlBuilder.ts +2 -1
- package/lib/validator.ts +150 -384
- package/loader/loadApis.ts +4 -7
- package/loader/loadHooks.ts +6 -5
- package/loader/loadPlugins.ts +11 -13
- package/main.ts +26 -53
- package/package.json +10 -8
- package/paths.ts +0 -6
- package/plugins/cipher.ts +1 -1
- package/plugins/config.ts +3 -4
- package/plugins/db.ts +6 -7
- package/plugins/jwt.ts +7 -6
- package/plugins/logger.ts +6 -6
- package/plugins/redis.ts +9 -13
- package/router/api.ts +2 -2
- package/router/static.ts +4 -8
- package/sync/syncAll.ts +8 -13
- package/sync/syncApi.ts +14 -10
- package/sync/syncDb/apply.ts +1 -2
- package/sync/syncDb.ts +12 -15
- package/sync/syncDev.ts +19 -56
- package/sync/syncMenu.ts +182 -137
- package/tests/cacheHelper.test.ts +327 -0
- package/tests/dbHelper-columns.test.ts +5 -20
- package/tests/dbHelper-execute.test.ts +14 -68
- package/tests/fields-redis-cache.test.ts +5 -3
- package/tests/integration.test.ts +17 -32
- package/tests/jwt.test.ts +36 -94
- package/tests/logger.test.ts +32 -34
- package/tests/redisHelper.test.ts +271 -2
- package/tests/redisKeys.test.ts +76 -0
- package/tests/sync-connection.test.ts +0 -6
- package/tests/syncDb-constants.test.ts +12 -12
- package/tests/util.test.ts +5 -1
- package/tests/validator.test.ts +611 -85
- package/types/befly.d.ts +9 -15
- package/types/cache.d.ts +73 -0
- package/types/common.d.ts +10 -128
- package/types/database.d.ts +221 -5
- package/types/index.ts +6 -5
- package/types/plugin.d.ts +1 -4
- package/types/redis.d.ts +37 -2
- package/types/table.d.ts +175 -0
- package/config.ts +0 -70
- package/hooks/_rateLimit.ts +0 -64
- package/lib/regexAliases.ts +0 -59
- package/lib/xml.ts +0 -383
- package/tests/validator-advanced.test.ts +0 -653
- package/tests/xml.test.ts +0 -101
- package/types/addon.d.ts +0 -50
- package/types/crypto.d.ts +0 -23
- package/types/jwt.d.ts +0 -99
- package/types/logger.d.ts +0 -43
- package/types/tool.d.ts +0 -67
- package/types/validator.d.ts +0 -43
package/lib/jwt.ts
CHANGED
|
@@ -1,457 +1,78 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JWT 工具类 - Befly 项目专用
|
|
3
|
-
* 直接集成环境变量,提供开箱即用的 JWT 功能
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { createHmac } from 'crypto';
|
|
7
|
-
import type { JwtPayload, JwtSignOptions, JwtVerifyOptions, JwtAlgorithm, JwtHeader, JwtDecoded } from '../types/jwt';
|
|
8
|
-
import type { AuthConfig } from '../types/befly';
|
|
9
|
-
|
|
10
1
|
/**
|
|
11
|
-
* JWT 工具类
|
|
2
|
+
* JWT 工具类 - 基于 fast-jwt 实现
|
|
12
3
|
*/
|
|
13
|
-
export class Jwt {
|
|
14
|
-
/** 默认配置 */
|
|
15
|
-
private static config: AuthConfig = {
|
|
16
|
-
secret: 'befly-secret',
|
|
17
|
-
expiresIn: '7d',
|
|
18
|
-
algorithm: 'HS256'
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* 配置 JWT
|
|
23
|
-
* @param config - JWT 配置
|
|
24
|
-
*/
|
|
25
|
-
static configure(config: AuthConfig) {
|
|
26
|
-
this.config = { ...this.config, ...config };
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** 算法映射 */
|
|
30
|
-
private static readonly ALGORITHMS: Record<JwtAlgorithm, string> = {
|
|
31
|
-
HS256: 'sha256',
|
|
32
|
-
HS384: 'sha384',
|
|
33
|
-
HS512: 'sha512'
|
|
34
|
-
};
|
|
35
4
|
|
|
36
|
-
|
|
37
|
-
* Base64 URL 编码
|
|
38
|
-
*/
|
|
39
|
-
static base64UrlEncode(input: string | Buffer): string {
|
|
40
|
-
const base64 = Buffer.isBuffer(input) ? input.toString('base64') : Buffer.from(input, 'utf8').toString('base64');
|
|
41
|
-
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Base64 URL 解码
|
|
46
|
-
*/
|
|
47
|
-
static base64UrlDecode(str: string): string {
|
|
48
|
-
let padding = 4 - (str.length % 4);
|
|
49
|
-
if (padding !== 4) str += '='.repeat(padding);
|
|
50
|
-
str = str.replace(/-/g, '+').replace(/_/g, '/');
|
|
51
|
-
return Buffer.from(str, 'base64').toString('utf8');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* 解析过期时间
|
|
56
|
-
* @param expiresIn - 过期时间(数字秒数或字符串如 "1h", "7d", "-1s")
|
|
57
|
-
* @returns 秒数(可以是负数)
|
|
58
|
-
*/
|
|
59
|
-
static parseExpiration(expiresIn: string | number): number {
|
|
60
|
-
if (typeof expiresIn === 'number') return expiresIn;
|
|
61
|
-
if (typeof expiresIn !== 'string') throw new Error('过期时间格式无效');
|
|
5
|
+
import { createSigner, createVerifier, createDecoder } from 'fast-jwt';
|
|
62
6
|
|
|
63
|
-
|
|
64
|
-
|
|
7
|
+
import type { AuthConfig } from '../types/befly.js';
|
|
8
|
+
import type { JwtPayload, JwtSignOptions, JwtVerifyOptions, JwtDecoded, JwtHeader } from 'befly-shared/types';
|
|
65
9
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
10
|
+
interface FastJwtComplete {
|
|
11
|
+
header: JwtHeader;
|
|
12
|
+
payload: JwtPayload;
|
|
13
|
+
signature: string;
|
|
14
|
+
input: string;
|
|
15
|
+
}
|
|
72
16
|
|
|
73
|
-
|
|
17
|
+
export class Jwt {
|
|
18
|
+
private config: AuthConfig;
|
|
74
19
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
w: 604800,
|
|
81
|
-
y: 31536000
|
|
20
|
+
constructor(config: AuthConfig = {}) {
|
|
21
|
+
this.config = {
|
|
22
|
+
secret: config.secret || 'befly-secret',
|
|
23
|
+
expiresIn: config.expiresIn || '7d',
|
|
24
|
+
algorithm: config.algorithm || 'HS256'
|
|
82
25
|
};
|
|
83
|
-
|
|
84
|
-
return value * multipliers[unit];
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* 创建签名
|
|
89
|
-
*/
|
|
90
|
-
private static createSignature(algorithm: JwtAlgorithm, secret: string, data: string): string {
|
|
91
|
-
const hashAlgorithm = this.ALGORITHMS[algorithm];
|
|
92
|
-
if (!hashAlgorithm) throw new Error(`不支持的算法: ${algorithm}`);
|
|
93
|
-
|
|
94
|
-
const hmac = createHmac(hashAlgorithm, secret);
|
|
95
|
-
hmac.update(data);
|
|
96
|
-
return this.base64UrlEncode(hmac.digest());
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* 常量时间比较(防止时序攻击)
|
|
101
|
-
*/
|
|
102
|
-
private static constantTimeCompare(a: string, b: string): boolean {
|
|
103
|
-
if (a.length !== b.length) return false;
|
|
104
|
-
let result = 0;
|
|
105
|
-
for (let i = 0; i < a.length; i++) {
|
|
106
|
-
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
107
|
-
}
|
|
108
|
-
return result === 0;
|
|
109
26
|
}
|
|
110
27
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
// 创建 header
|
|
125
|
-
const header = Jwt.base64UrlEncode(
|
|
126
|
-
JSON.stringify({
|
|
127
|
-
alg: algorithm,
|
|
128
|
-
typ: 'JWT'
|
|
129
|
-
})
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
// 创建 payload
|
|
133
|
-
const jwtPayload: JwtPayload = { ...payload, iat: now };
|
|
134
|
-
|
|
135
|
-
if (expiresIn) {
|
|
136
|
-
const expSeconds = Jwt.parseExpiration(expiresIn);
|
|
137
|
-
jwtPayload.exp = now + expSeconds;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (options.issuer) jwtPayload.iss = options.issuer;
|
|
141
|
-
if (options.audience) jwtPayload.aud = options.audience;
|
|
142
|
-
if (options.subject) jwtPayload.sub = options.subject;
|
|
143
|
-
if (options.notBefore) {
|
|
144
|
-
jwtPayload.nbf = typeof options.notBefore === 'number' ? options.notBefore : now + Jwt.parseExpiration(options.notBefore);
|
|
145
|
-
}
|
|
146
|
-
if (options.jwtId) jwtPayload.jti = options.jwtId;
|
|
147
|
-
|
|
148
|
-
const encodedPayload = Jwt.base64UrlEncode(JSON.stringify(jwtPayload));
|
|
149
|
-
|
|
150
|
-
// 创建签名
|
|
151
|
-
const data = `${header}.${encodedPayload}`;
|
|
152
|
-
const signature = Jwt.createSignature(algorithm, secret, data);
|
|
153
|
-
|
|
154
|
-
return `${data}.${signature}`;
|
|
28
|
+
sign(payload: JwtPayload, options: JwtSignOptions = {}): string {
|
|
29
|
+
const signer = createSigner({
|
|
30
|
+
key: options.secret || this.config.secret,
|
|
31
|
+
algorithm: options.algorithm || this.config.algorithm,
|
|
32
|
+
expiresIn: options.expiresIn || this.config.expiresIn,
|
|
33
|
+
iss: options.issuer,
|
|
34
|
+
aud: options.audience,
|
|
35
|
+
sub: options.subject,
|
|
36
|
+
jti: options.jwtId,
|
|
37
|
+
notBefore: options.notBefore
|
|
38
|
+
});
|
|
39
|
+
return signer(payload);
|
|
155
40
|
}
|
|
156
41
|
|
|
157
|
-
|
|
158
|
-
* 验证 Token
|
|
159
|
-
* @param token - Token 字符串
|
|
160
|
-
* @param options - 验证选项
|
|
161
|
-
* @returns 解码后的载荷
|
|
162
|
-
*/
|
|
163
|
-
static async verify<T = JwtPayload>(token: string, options: JwtVerifyOptions = {}): Promise<T> {
|
|
42
|
+
verify<T = JwtPayload>(token: string, options: JwtVerifyOptions = {}): T {
|
|
164
43
|
if (!token || typeof token !== 'string') {
|
|
165
44
|
throw new Error('Token必须是非空字符串');
|
|
166
45
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// 解码 payload
|
|
187
|
-
const payloadStr = Jwt.base64UrlDecode(payloadB64);
|
|
188
|
-
let payload: JwtPayload;
|
|
189
|
-
try {
|
|
190
|
-
payload = JSON.parse(payloadStr);
|
|
191
|
-
} catch (e) {
|
|
192
|
-
throw new Error('Token 载荷无效');
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// 验证过期时间
|
|
196
|
-
if (!options.ignoreExpiration) {
|
|
197
|
-
const now = Math.floor(Date.now() / 1000);
|
|
198
|
-
if (payload.exp && payload.exp < now) {
|
|
199
|
-
throw new Error('Token 已过期');
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// 验证其他声明
|
|
204
|
-
if (options.issuer && payload.iss !== options.issuer) {
|
|
205
|
-
throw new Error('Token issuer 无效');
|
|
206
|
-
}
|
|
207
|
-
if (options.audience && payload.aud !== options.audience) {
|
|
208
|
-
throw new Error('Token audience 无效');
|
|
209
|
-
}
|
|
210
|
-
if (options.subject && payload.sub !== options.subject) {
|
|
211
|
-
throw new Error('Token subject 无效');
|
|
212
|
-
}
|
|
213
|
-
if (options.jwtId && payload.jti !== options.jwtId) {
|
|
214
|
-
throw new Error('Token jwtId 无效');
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
return payload as T;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* 解码 JWT token (不验证签名)
|
|
222
|
-
* @param token - JWT token字符串
|
|
223
|
-
* @param complete - 是否返回完整信息(包含header)
|
|
224
|
-
* @returns 解码后的内容
|
|
225
|
-
*/
|
|
226
|
-
static decode(token: string, complete?: false): JwtPayload;
|
|
227
|
-
static decode(token: string, complete: true): JwtDecoded;
|
|
228
|
-
static decode(token: string, complete: boolean = false): JwtPayload | JwtDecoded {
|
|
46
|
+
const verifier = createVerifier({
|
|
47
|
+
key: options.secret || this.config.secret,
|
|
48
|
+
algorithms: options.algorithms || [this.config.algorithm || 'HS256'],
|
|
49
|
+
allowedIss: options.issuer,
|
|
50
|
+
allowedAud: options.audience,
|
|
51
|
+
allowedSub: options.subject,
|
|
52
|
+
ignoreExpiration: options.ignoreExpiration,
|
|
53
|
+
ignoreNotBefore: options.ignoreNotBefore,
|
|
54
|
+
cache: true,
|
|
55
|
+
cacheTTL: 600000
|
|
56
|
+
});
|
|
57
|
+
return verifier(token) as T;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
decode(token: string, complete?: false): JwtPayload;
|
|
61
|
+
decode(token: string, complete: true): JwtDecoded;
|
|
62
|
+
decode(token: string, complete: boolean = false): JwtPayload | JwtDecoded {
|
|
229
63
|
if (!token || typeof token !== 'string') {
|
|
230
64
|
throw new Error('Token必须是非空字符串');
|
|
231
65
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
} catch (error: any) {
|
|
244
|
-
throw new Error('JWT解码失败: ' + error.message);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* 获取 token 剩余有效时间
|
|
250
|
-
* @param token - JWT token字符串
|
|
251
|
-
* @returns 剩余秒数,-1表示已过期或无过期时间
|
|
252
|
-
*/
|
|
253
|
-
static getTimeToExpiry(token: string): number {
|
|
254
|
-
try {
|
|
255
|
-
const payload = this.decode(token);
|
|
256
|
-
if (!payload.exp) return -1;
|
|
257
|
-
const remaining = payload.exp - Math.floor(Date.now() / 1000);
|
|
258
|
-
return remaining > 0 ? remaining : -1;
|
|
259
|
-
} catch {
|
|
260
|
-
return -1;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* 检查 token 是否已过期
|
|
266
|
-
* @param token - JWT token字符串
|
|
267
|
-
* @returns 是否已过期
|
|
268
|
-
*/
|
|
269
|
-
static isExpired(token: string): boolean {
|
|
270
|
-
return this.getTimeToExpiry(token) <= 0;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* 检查 token 是否即将过期
|
|
275
|
-
* @param token - JWT token字符串
|
|
276
|
-
* @param thresholdSeconds - 过期阈值(秒),默认300秒(5分钟)
|
|
277
|
-
* @returns 是否即将过期
|
|
278
|
-
*/
|
|
279
|
-
static isNearExpiry(token: string, thresholdSeconds: number = 300): boolean {
|
|
280
|
-
const timeToExpiry = this.getTimeToExpiry(token);
|
|
281
|
-
return timeToExpiry > 0 && timeToExpiry <= thresholdSeconds;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* 检查载荷是否包含特定角色
|
|
286
|
-
* @param payload - JWT载荷
|
|
287
|
-
* @param role - 要检查的角色
|
|
288
|
-
* @returns 是否包含该角色
|
|
289
|
-
*/
|
|
290
|
-
static hasRole(payload: JwtPayload, role: string): boolean {
|
|
291
|
-
if (!payload) return false;
|
|
292
|
-
if (payload.role === role) return true;
|
|
293
|
-
if (Array.isArray(payload.roles) && payload.roles.includes(role)) return true;
|
|
294
|
-
return false;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* 检查载荷是否包含任意一个角色
|
|
299
|
-
* @param payload - JWT载荷
|
|
300
|
-
* @param roles - 要检查的角色列表
|
|
301
|
-
* @returns 是否包含任意一个角色
|
|
302
|
-
*/
|
|
303
|
-
static hasAnyRole(payload: JwtPayload, roles: string[]): boolean {
|
|
304
|
-
if (!payload) return false;
|
|
305
|
-
return roles.some((role) => this.hasRole(payload, role));
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* 检查载荷是否包含特定权限
|
|
310
|
-
* @param payload - JWT载荷
|
|
311
|
-
* @param permission - 要检查的权限
|
|
312
|
-
* @returns 是否包含该权限
|
|
313
|
-
*/
|
|
314
|
-
static hasPermission(payload: JwtPayload, permission: string): boolean {
|
|
315
|
-
if (!payload || !payload.permissions) return false;
|
|
316
|
-
return Array.isArray(payload.permissions) && payload.permissions.includes(permission);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* 检查载荷是否包含所有权限
|
|
321
|
-
* @param payload - JWT载荷
|
|
322
|
-
* @param permissions - 要检查的权限列表
|
|
323
|
-
* @returns 是否包含所有权限
|
|
324
|
-
*/
|
|
325
|
-
static hasAllPermissions(payload: JwtPayload, permissions: string[]): boolean {
|
|
326
|
-
if (!payload || !payload.permissions) return false;
|
|
327
|
-
return permissions.every((permission) => this.hasPermission(payload, permission));
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// ==================== 便捷方法(自动使用环境变量) ====================
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* 创建用户 token(快捷方法)
|
|
334
|
-
*/
|
|
335
|
-
static async create(payload: JwtPayload): Promise<string> {
|
|
336
|
-
return this.sign(payload);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* 检查 token(快捷方法)
|
|
341
|
-
*/
|
|
342
|
-
static async check(token: string): Promise<JwtPayload> {
|
|
343
|
-
return this.verify(token);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* 解析 token(快捷方法,不验证签名)
|
|
348
|
-
*/
|
|
349
|
-
static parse(token: string): JwtPayload {
|
|
350
|
-
return this.decode(token);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* 签名用户认证 token
|
|
355
|
-
*/
|
|
356
|
-
static async signUserToken(userInfo: JwtPayload, options?: JwtSignOptions): Promise<string> {
|
|
357
|
-
return this.sign(userInfo, options);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* 签名 API 访问 token
|
|
362
|
-
*/
|
|
363
|
-
static async signAPIToken(payload: JwtPayload, options?: JwtSignOptions): Promise<string> {
|
|
364
|
-
return this.sign(payload, { audience: 'api', expiresIn: '1h', ...options });
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* 签名刷新 token
|
|
369
|
-
*/
|
|
370
|
-
static async signRefreshToken(payload: JwtPayload, options?: JwtSignOptions): Promise<string> {
|
|
371
|
-
return this.sign(payload, { audience: 'refresh', expiresIn: '30d', ...options });
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* 签名临时 token (用于重置密码等)
|
|
376
|
-
*/
|
|
377
|
-
static async signTempToken(payload: JwtPayload, options?: JwtSignOptions): Promise<string> {
|
|
378
|
-
return this.sign(payload, { audience: 'temporary', expiresIn: '15m', ...options });
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* 验证用户认证 token
|
|
383
|
-
*/
|
|
384
|
-
static async verifyUserToken(token: string, options?: JwtVerifyOptions): Promise<JwtPayload> {
|
|
385
|
-
return this.verify(token, options);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* 验证 API 访问 token
|
|
390
|
-
*/
|
|
391
|
-
static async verifyAPIToken(token: string, options?: JwtVerifyOptions): Promise<JwtPayload> {
|
|
392
|
-
return this.verify(token, { audience: 'api', ...options });
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* 验证刷新 token
|
|
397
|
-
*/
|
|
398
|
-
static async verifyRefreshToken(token: string, options?: JwtVerifyOptions): Promise<JwtPayload> {
|
|
399
|
-
return this.verify(token, { audience: 'refresh', ...options });
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* 验证临时 token
|
|
404
|
-
*/
|
|
405
|
-
static async verifyTempToken(token: string, options?: JwtVerifyOptions): Promise<JwtPayload> {
|
|
406
|
-
return this.verify(token, { audience: 'temporary', ...options });
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* 验证 token 并检查权限
|
|
411
|
-
*/
|
|
412
|
-
static async verifyWithPermissions(token: string, requiredPermissions: string | string[], options?: JwtVerifyOptions): Promise<JwtPayload> {
|
|
413
|
-
const payload = await this.verify(token, options);
|
|
414
|
-
|
|
415
|
-
if (!payload.permissions) {
|
|
416
|
-
throw new Error('Token中不包含权限信息');
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
const permissions = Array.isArray(requiredPermissions) ? requiredPermissions : [requiredPermissions];
|
|
420
|
-
const hasPermission = permissions.every((permission) => payload.permissions.includes(permission));
|
|
421
|
-
|
|
422
|
-
if (!hasPermission) {
|
|
423
|
-
throw new Error('权限不足');
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
return payload;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
/**
|
|
430
|
-
* 验证 token 并检查角色
|
|
431
|
-
*/
|
|
432
|
-
static async verifyWithRoles(token: string, requiredRoles: string | string[], options?: JwtVerifyOptions): Promise<JwtPayload> {
|
|
433
|
-
const payload = await this.verify(token, options);
|
|
434
|
-
|
|
435
|
-
if (!payload.role && !payload.roles) {
|
|
436
|
-
throw new Error('Token中不包含角色信息');
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
const userRoles = payload.roles || [payload.role];
|
|
440
|
-
const roles = Array.isArray(requiredRoles) ? requiredRoles : [requiredRoles];
|
|
441
|
-
|
|
442
|
-
const hasRole = roles.some((role) => userRoles.includes(role));
|
|
443
|
-
|
|
444
|
-
if (!hasRole) {
|
|
445
|
-
throw new Error('角色权限不足');
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
return payload;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* 软验证 token (忽略过期时间)
|
|
453
|
-
*/
|
|
454
|
-
static async verifySoft(token: string, options?: JwtVerifyOptions): Promise<JwtPayload> {
|
|
455
|
-
return this.verify(token, { ignoreExpiration: true, ...options });
|
|
66
|
+
if (complete) {
|
|
67
|
+
const decoder = createDecoder({ complete: true });
|
|
68
|
+
const result = decoder(token) as FastJwtComplete;
|
|
69
|
+
return {
|
|
70
|
+
header: result.header,
|
|
71
|
+
payload: result.payload,
|
|
72
|
+
signature: result.signature
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const decoder = createDecoder();
|
|
76
|
+
return decoder(token) as JwtPayload;
|
|
456
77
|
}
|
|
457
78
|
}
|