Passport.js 集成 Express:认证实战指南

Passport.js 是 Node.js 最流行的认证中间件,500+ 策略覆盖本地登录与社交 OAuth。本文实战 Local 策略注册登录、会话管理与路由保护。

最佳实践
应用接口与数据存储连接插画

Passport 的核心是策略(Strategy)模式:passport.use(new LocalStrategy(verifyFn)) 注册验证逻辑,passport.authenticate('local') 中间件守卫路由。配 express-session 维持登录态,req.user 在后续请求中可用。

中间件准备

npm install express express-session passport passport-local bcrypt
const session = require('express-session');
const passport = require('passport');

app.use(express.json());
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: { secure: 'auto', maxAge: 24 * 60 * 60 * 1000 },
}));
app.use(passport.initialize());
app.use(passport.session());

配置 Local 策略

const LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcrypt');

passport.use(new LocalStrategy(
  { usernameField: 'email' },
  async (email, password, done) => {
    try {
      const user = await db.users.findByEmail(email);
      if (!user) return done(null, false, { message: '邮箱或密码错误' });
      const ok = await bcrypt.compare(password, user.passwordHash);
      if (!ok) return done(null, false, { message: '邮箱或密码错误' });
      return done(null, user);
    } catch (err) {
      return done(err);
    }
  }
));

// 会话序列化:只存用户 ID
passport.serializeUser((user, done) => done(null, user.id));
passport.deserializeUser(async (id, done) => {
  try { done(null, await db.users.findById(id)); }
  catch (err) { done(err); }
});

done 的三态:done(null, user) 成功;done(null, false, message) 认证失败;done(err) 系统错误——语义区分是 Passport 的契约核心。

认证路由

app.post('/auth/register', async (req, res) => {
  const { email, password } = req.body;
  if (await db.users.findByEmail(email)) {
    return res.status(409).json({ error: '邮箱已注册' });
  }
  const passwordHash = await bcrypt.hash(password, 12);
  const user = await db.users.create({ email, passwordHash });
  req.login(user, () => res.status(201).json({ id: user.id, email }));
});

app.post('/auth/login', passport.authenticate('local'), (req, res) => {
  res.json({ id: req.user.id, email: req.user.email });
});

app.post('/auth/logout', (req, res) => {
  req.logout(() => res.json({ ok: true }));
});

路由保护

function requireAuth(req, res, next) {
  if (req.isAuthenticated()) return next();
  res.status(401).json({ error: '请先登录' });
}

app.get('/api/profile', requireAuth, (req, res) => res.json(req.user));

安全清单

  • 密码 bcrypt 哈希(cost ≥ 12),绝不明文;
  • session 的 secret 用环境变量,生产 cookie 开 secure + httpOnly;
  • 登录接口加限流(防暴力破解)与验证码;
  • 会话存储生产用 Redis(connect-redis)——内存存储重启即丢失。

常见问题(FAQ)

Q:Passport 适合 REST API + JWT 吗?

可以(passport-jwt 策略)。但想清楚:纯 API + SPA 场景 JWT 无状态更方便;传统服务端渲染或需要服务端登出控制用 session。Passport 两种都支持。

Q:500 多个策略都靠谱吗?

官方维护的核心策略(local/oauth/google/github)质量可靠;社区长尾策略质量参差——用前看维护状态与周下载量。

Q:现代替代方案?

NestJS 生态用 @nestjs/passport 包装;新框架(Hono 等)有自己的认证中间件。Express 存量项目 Passport 仍是标准答案。


参考文档

本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。

延伸阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台