You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express.js中Passport.js JWT中间件查询用户状态是否合理?

问题背景

我开发了一个Node.js应用,采用Passport.js的JWT策略。向客户端发放JWT后,用户可能会注销账号,但由于已提前获取JWT,仍可在JWT过期前继续请求其他接口。想知道在Passport.js中间件层通过额外数据库查询检查用户状态是否合理,或是存在其他解决方案?

原代码片段:

const passportStrategy = (passport: {
  use: (arg0: passportJWT.Strategy) => void;
}) => {
  passport.use(
    new JWTStrategy(
      {
        jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
        secretOrKey: process.env.JWT_AUTH_KEY!,
        passReqToCallback: true,
      },
      async (
        req: Request,
        payload: any,
        done: passportJWT.VerifiedCallback
      ) => {
        const src = DataSourceFactory.source;
         //db query in here

        try {
          
          });

          
          return done(null, user);
        } catch (findUserError) {
          return done(findUserError);
        }
      }
    )
  );
};
解决方案分析

一、中间件层加数据库查询的合理性

这种方案完全合理,是处理即时注销需求的直接手段:

  • 优势:逻辑简单直接,能确保每次请求都验证用户当前状态,不会出现已注销用户仍能操作的情况。
  • 劣势:每次请求都增加数据库查询,会提升系统开销,高并发场景下可能影响性能。如果你的应用并发量不高,或者对安全性的优先级高于性能,这就是最优选择。

补全后的代码示例:

const passportStrategy = (passport: {
  use: (arg0: passportJWT.Strategy) => void;
}) => {
  passport.use(
    new JWTStrategy(
      {
        jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
        secretOrKey: process.env.JWT_AUTH_KEY!,
        passReqToCallback: true,
      },
      async (
        req: Request,
        payload: any,
        done: passportJWT.VerifiedCallback
      ) => {
        const src = DataSourceFactory.source;

        try {
          // 仅查询用户ID和状态字段,减少数据库开销
          const user = await src.getRepository(User).findOne({
            where: { id: payload.userId },
            select: ['id', 'isActive']
          });

          if (!user || !user.isActive) {
            // 用户不存在或已注销,返回认证失败
            return done(null, false, { message: '用户已注销或不存在' });
          }

          return done(null, user);
        } catch (findUserError) {
          return done(findUserError);
        }
      }
    )
  );
};

二、其他替代方案

1. 缩短JWT过期时间 + 刷新令牌机制

  • 核心思路:把JWT的过期时间设得很短(比如15分钟),同时发放一个有效期更长的刷新令牌。用户需要用刷新令牌定期获取新的JWT。
  • 注销时,直接将刷新令牌加入黑名单,这样用户无法获取新的JWT,旧JWT过期后就无法继续操作。
  • 优势:大幅减少数据库查询次数,只有在刷新令牌时才需要验证状态或黑名单,性能更优。
  • 注意:刷新令牌要存在数据库或Redis中,注销时标记为无效。

2. 维护JWT黑名单

  • 核心思路:用户注销时,将当前有效的JWT加入黑名单(推荐存Redis,过期时间和JWT保持一致)。
  • 在Passport中间件中,先验证JWT签名,再检查该JWT是否在黑名单中。
  • 优势:避免每次查询用户状态,Redis的查询性能远高于数据库,且能自动清理过期的黑名单数据。

3. 敏感接口单独校验用户状态

  • 核心思路:JWT设中等过期时间(比如1小时),只在关键接口(如支付、修改个人信息)中查询用户状态,普通接口依赖JWT过期时间自动失效。
  • 优势:平衡安全性和性能,既保证高敏感操作的安全性,又减少普通请求的开销。
总结
  • 如果对即时注销要求极高且并发量不大,直接在Passport中间件加数据库查询是最稳妥的选择;
  • 若追求性能,优先考虑短JWT+刷新令牌+黑名单的组合;
  • 敏感接口单独做状态验证,普通接口依赖JWT过期,适合大多数中型应用场景。

内容的提问来源于stack exchange,提问作者Nurettin Şenssabc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 12:21:22