feat: 更新 N5Proxy 以支持生成带有刷新令牌的 JWKS 令牌

This commit is contained in:
xiongxiao
2026-03-18 22:31:31 +08:00
committed by cnb
parent 08884b7e4b
commit 6467e6dea8
2 changed files with 15 additions and 13 deletions

View File

@@ -61,6 +61,8 @@ type StoreSetOpts = {
loginType?: 'default' | 'plugin' | 'month' | 'season' | 'year' | 'week' | 'day'; // 登陆类型 'default' | 'plugin' | 'month' | 'season' | 'year' loginType?: 'default' | 'plugin' | 'month' | 'season' | 'year' | 'week' | 'day'; // 登陆类型 'default' | 'plugin' | 'month' | 'season' | 'year'
expire?: number; // 过期时间,单位为秒 expire?: number; // 过期时间,单位为秒
hasRefreshToken?: boolean; hasRefreshToken?: boolean;
// refreshToken的过期时间比accessToken多多少天默认是1天
expireDay?: number;
[key: string]: any; [key: string]: any;
}; };
interface Store<T> { interface Store<T> {
@@ -70,7 +72,7 @@ interface Store<T> {
expire: (key: string, ttl?: number) => Promise<void>; expire: (key: string, ttl?: number) => Promise<void>;
delObject: (value?: T) => Promise<void>; delObject: (value?: T) => Promise<void>;
keys: (key?: string) => Promise<string[]>; keys: (key?: string) => Promise<string[]>;
setToken: (value: { accessToken: string; refreshToken: string; value?: T }, opts?: StoreSetOpts) => Promise<TokenData>; setToken: (value: { accessToken: string; refreshToken: string; value?: T, day?: number }, opts?: StoreSetOpts) => Promise<TokenData>;
delKeys: (keys: string[]) => Promise<number>; delKeys: (keys: string[]) => Promise<number>;
} }
@@ -138,9 +140,11 @@ export class RedisTokenStore implements Store<OauthUser> {
await this.del(userPrefix + ':token:' + accessToken); await this.del(userPrefix + ':token:' + accessToken);
} }
} }
async setToken(data: { accessToken: string; refreshToken: string; value?: OauthUser }, opts?: StoreSetOpts): Promise<TokenData> { async setToken(data: { accessToken: string; refreshToken: string; value?: OauthUser, day?: number }, opts?: StoreSetOpts): Promise<TokenData> {
const { accessToken, refreshToken, value } = data; const { accessToken, refreshToken, value } = data;
let userPrefix = 'user:' + value?.id; let userPrefix = 'user:' + value?.id;
const expireDay = data?.day || 1;
if (value?.orgId) { if (value?.orgId) {
userPrefix = 'org:' + value?.orgId + ':user:' + value?.id; userPrefix = 'org:' + value?.orgId + ':user:' + value?.id;
} }
@@ -171,13 +175,9 @@ export class RedisTokenStore implements Store<OauthUser> {
await this.set(accessToken, JSON.stringify(value), expire); await this.set(accessToken, JSON.stringify(value), expire);
await this.set(userPrefix + ':token:' + accessToken, accessToken, expire); await this.set(userPrefix + ':token:' + accessToken, accessToken, expire);
// refreshToken的过期时间比accessToken多2确保在accessToken过期后refreshToken仍然有效 // refreshToken的过期时间比accessToken多expireDay确保在accessToken过期后refreshToken仍然有效
let refreshTokenExpiresIn = expire + 2 * day; let refreshTokenExpiresIn = expire + expireDay * day;
if (refreshToken) { if (refreshToken) {
// 小于7天, 则设置为7天
if (refreshTokenExpiresIn < 60 * 60 * 24 * 7) {
refreshTokenExpiresIn = 60 * 60 * 24 * 7;
}
await this.set(refreshToken, JSON.stringify(value), refreshTokenExpiresIn); await this.set(refreshToken, JSON.stringify(value), refreshTokenExpiresIn);
await this.set(userPrefix + ':refreshToken:' + refreshToken, refreshToken, refreshTokenExpiresIn); await this.set(userPrefix + ':refreshToken:' + refreshToken, refreshToken, refreshTokenExpiresIn);
} }
@@ -239,7 +239,7 @@ export class OAuth<T extends OauthUser> {
user.oauthExpand.refreshToken = refreshToken; user.oauthExpand.refreshToken = refreshToken;
} }
} }
const tokenData = await this.store.setToken({ accessToken, refreshToken, value: user }, expandOpts); const tokenData = await this.store.setToken({ accessToken, refreshToken, value: user, day: expandOpts?.day }, expandOpts);
return tokenData; return tokenData;
} }
@@ -253,7 +253,7 @@ export class OAuth<T extends OauthUser> {
createTime: new Date().getTime(), // 创建时间 createTime: new Date().getTime(), // 创建时间
}; };
await this.store.setToken( await this.store.setToken(
{ accessToken: secretKey, refreshToken: '', value: oauthUser }, { accessToken: secretKey, refreshToken: '', value: oauthUser, day: opts?.day },
{ {
...opts, ...opts,
hasRefreshToken: false, hasRefreshToken: false,
@@ -338,6 +338,7 @@ export class OAuth<T extends OauthUser> {
{ {
...user.oauthExpand, ...user.oauthExpand,
hasRefreshToken: true, hasRefreshToken: true,
day: user.oauthExpand?.day,
}, },
); );
console.log('resetToken token', await this.store.keys()); console.log('resetToken token', await this.store.keys());
@@ -370,6 +371,7 @@ export class OAuth<T extends OauthUser> {
{ {
...user.oauthExpand, ...user.oauthExpand,
hasRefreshToken: true, hasRefreshToken: true,
day: user.oauthExpand?.day,
}, },
); );
@@ -429,8 +431,8 @@ export class OAuth<T extends OauthUser> {
async setJwksToken(token: string, opts: { id: string; expire: number }) { async setJwksToken(token: string, opts: { id: string; expire: number }) {
const expire = opts.expire ?? 2 * 3600; // 2 hours const expire = opts.expire ?? 2 * 3600; // 2 hours
const id = opts.id || '-'; const id = opts.id || '-';
// jwks token的过期时间比accessToken多3天,确保3天内可以用来refresh token // jwks token的过期时间比accessToken多2天,确保2天内可以用来refresh token
const addExpire = 3 * 24 * 3600; const addExpire = 2 * 24 * 3600;
await this.store.redis.set('user:jwks:' + token, id, 'EX', expire + addExpire); await this.store.redis.set('user:jwks:' + token, id, 'EX', expire + addExpire);
} }
async deleteJwksToken(token: string) { async deleteJwksToken(token: string) {

View File

@@ -48,7 +48,7 @@ export const N5Proxy = async (req: IncomingMessage, res: ServerResponse, opts?:
} }
try { try {
const user = await User.findByPk(userId); const user = await User.findByPk(userId);
const token = await User.createJwksTokenResponse({ id: userId, username: user?.username || '' }, { hasRefreshToken: false }); const token = await User.createJwksTokenResponse({ id: userId, username: user?.username || '' }, { hasRefreshToken: true });
const urlObj = new URL(link); const urlObj = new URL(link);
urlObj.searchParams.set('token', token.accessToken); urlObj.searchParams.set('token', token.accessToken);
const resultLink = await fetch(urlObj.toString(), { method: 'GET' }).then(res => res.json()) const resultLink = await fetch(urlObj.toString(), { method: 'GET' }).then(res => res.json())