首页
/ freeCodeCamp Advanced Node and Express:用 Passport LocalStrategy 实现本地认证策略(Authentication Strategies)

freeCodeCamp Advanced Node and Express:用 Passport LocalStrategy 实现本地认证策略(Authentication Strategies)

2026-09-04 12:34:20作者:庞眉杨Will

本篇围绕 freeCodeCamp 课程中 Advanced Node and Express 项目的一个核心关卡 "Authentication Strategies" 展开:讲解如何用 Passport 的 passport-local 策略实现"用户名 + 密码"的本地认证,包括 LocalStrategy 的注册方式、done 回调的三种返回语义、为什么策略代码必须放在数据库连接回调内,以及自动化测试是如何校验你的实现的。读完后你能把这套认证策略完整跑通,并理解它在整个登录链路(/login 路由 → 中间件 → 会话)中的位置。

在课程项目中的位置

该关卡属于 freeCodeCamp 课程仓库中"质量保证与测试"(Quality Assurance)超级块下的 Advanced Node and Express 项目块,关卡定义见 关卡文档,块内关卡顺序见 advanced-node-and-express.json

在关卡顺序中,它排在完成模板引擎(Pug)、配置 Passport 与 express-session、实现用户序列化(serializeUser / deserializeUser)和数据库连接之后。也就是说,到达本关卡时你的项目已经具备:

  • pug 视图引擎(app.set('view engine', 'pug')views 指向 ./views/pug);
  • express-session 会话(secret 取自 process.env.SESSION_SECRETresave: truesaveUninitialized: truecookie: { secure: false }),以及 app.use(passport.initialize())app.use(passport.session())
  • passport.serializeUser / passport.deserializeUser,并且序列化/反序列化代码以及路由都包裹在 myDB(async client => { ... }) 的数据库连接回调中(见 Set up PassportImplement the Serialization of a Passport User)。

本关卡的任务是补上"认证策略"这块拼图:告诉 Passport 如何 验证一个用户,而不是 在哪个路由上 调用验证(后者是下一关 "How to Use Passport Strategies" 的内容)。

什么是策略(Strategy)

按原文档的定义:策略是一种认证用户的方式。你可以基于本地保存的信息(前提是先让用户注册过)来认证,也可以依赖 Google、GitHub 等第三方提供商。本项目使用 Passport 中间件——Passport 提供了一整套策略,支持用户名/密码、GitHub、Google 等多种认证方式。

本关卡用到的策略是 passport-local@~1.0.0(课程模板已预先作为依赖写入 package.json),你只需要在自己的服务器代码中引入它:

const LocalStrategy = require('passport-local');

注册 LocalStrategy:完整代码与逐行解析

原文档要求用 passport.use 注册一个实例化后的 LocalStrategy 对象,并且特别强调:从这一步起的代码都要封装在数据库连接回调内,因为策略依赖数据库。完整写法如下:

passport.use(new LocalStrategy((username, password, done) => {
  myDataBase.findOne({ username: username }, (err, user) => {
    console.log(`User ${username} attempted to log in.`);
    if (err) return done(err);
    if (!user) return done(null, false);
    if (password !== user.password) return done(null, false);
    return done(null, user);
  });
}));

这段代码定义了"本地认证"的完整流程:先用用户输入的 username 去数据库查用户;再比对密码;只要没有命中错误分支,就把 user 对象交还给 Passport,用户即被认证通过。

逐行看 done 回调的三种调用语义,这是理解 Passport 本地策略的关键:

调用 含义 本例对应场景
done(err) 认证过程本身出错(例如数据库查询失败) findOne 返回 err
done(null, false) 凭证无效:用户不存在或密码不匹配 !userpassword !== user.password
done(null, user) 认证成功,把完整用户对象交给 Passport 用户名、密码都匹配

注意密码比对采用的是明文全等比较(password !== user.password)。这并不是疏漏——课程在后面的 "Hashing Your Passwords" 关卡(关卡文档)会明确要求引入 bcrypt@~5.0.0,把这一行替换为:

if (!bcrypt.compareSync(password, user.password)) {
  return done(null, false);
}

并在注册路由用 bcrypt.hashSync(req.body.password, 12) 生成哈希后再入库。也就是说,本关卡先建立"策略骨架",后续关卡再加固密码存储这一安全环节。

为什么必须放在数据库连接回调里

原文档用加粗文字强调"make sure this (as well as everything from this point on) is encapsulated in the database connection"。原因在于:策略的 LocalStrategy 回调中调用了 myDataBase.findOne,而 myDataBase 这个集合句柄是在 myDB(async client => { ... }) 回调内通过 client.db('database').collection('users') 创建的。从源码结构看,把 passport.use(...) 放在回调之外,myDataBase 要么未定义、要么指向已关闭的旧连接。因此正确结构是:

myDB(async client => {
  const myDataBase = await client.db('database').collection('users');

  // 序列化、反序列化也在这里
  passport.serializeUser((user, done) => { done(null, user._id); });
  passport.deserializeUser((id, done) => {
    myDataBase.findOne({ _id: new ObjectID(id) }, (err, doc) => {
      done(null, doc);
    });
  });

  // 本关卡:注册 LocalStrategy
  passport.use(new LocalStrategy((username, password, done) => {
    myDataBase.findOne({ username: username }, (err, user) => {
      if (err) return done(err);
      if (!user) return done(null, false);
      if (password !== user.password) return done(null, false);
      return done(null, user);
    });
  }));

  // 后续路由(/login 等)也在这里
}).catch(e => {
  // 连接失败时渲染错误页
});
// app.listen 放在 myDB 回调之外

其他策略:以 GitHub 策略为例

原文档还指出:很多策略都通过各自不同的配置项搭建,通常参照该策略仓库的 README 即可轻松配置。它举了 GitHub 策略的例子——你不需要关心用户名或密码,因为用户会被跳转到 GitHub 的认证页;只要用户在 GitHub 处于登录状态并同意授权,GitHub 就会把用户资料返回给你使用。块内后续的三个 "Implementation of Social Authentication" 关卡(见 关卡顺序)正是把这一思路落地。

自动化测试如何校验你的实现

本关卡是 challengeType: 2(项目型关卡),测试代码内置在关卡文档的 --hints-- 区块中(原文档 的 L41-L77)。它通过 /_api/package.json/_api/server.js 两个内部端点拉取你的代码做静态断言:

  1. 依赖检查package.jsondependencies 中必须列出 passport-local
  2. 引入检查server.js 中必须匹配 /require.*("|')passport-local("|')/,即确实 require('passport-local')
  3. 策略注册检查:必须出现 new LocalStrategyassert.match(data, /new LocalStrategy/)),证明你告诉 Passport 使用了一个新策略;
  4. 数据库查询检查:必须出现 findOneassert.match(data, /findOne/)),证明本地策略是基于输入的用户名执行 findOne 查询的,而不是硬编码用户。

对照原文档的验收标准:passport-local 是依赖、策略已实例化并注册、策略内使用 findOne 查询用户名——三条全部满足后,本关卡才算完成。

与下一关的衔接:策略注册与策略调用是分开的两步

本关卡末尾原文档预告:"在下一步,你将设置如何真正调用认证策略来基于表单数据验证用户。" 在下一关 How to Use Passport Strategies 中:

  • index.pug 里的登录表单默认被 if showLogin 隐藏,需要在 res.render 时传入 showLogin: true 才会显示;
  • 表单 POST/login,该路由需要挂载中间件 passport.authenticate('local', { failureRedirect: '/' }),认证通过后才执行重定向到 /profile 的响应;
  • 认证成功时,完整用户对象保存在 req.user 上(这正是前面序列化/反序列化配合会话 cookie 的工作成果)。

在尚未实现注册流程之前,对 /login 的 POST 请求会失败并重定向回首页 /,服务端控制台会打印 User {USERNAME} attempted to log in.——这是验证本关策略代码已生效的最直接信号(策略回调执行了,只是查不到已注册用户)。

小结与参考

本关卡在整条认证链路中的职责可以概括为:

  • 策略(本关):定义"怎样判定一个用户通过"——passport.use(new LocalStrategy(...)) + findOne + 密码比对 + 三种 done 语义;
  • 调用(下一关):在 /login POST 路由上用 passport.authenticate('local') 触发策略,failureRedirect 处理失败分支;
  • 会话与持久化(前置关卡):express-session + 序列化保证"登录一次,后续请求免验证";
  • 安全加固(后续关卡)bcrypt 哈希替换明文比对。

核心参考文件:

说明:以上内容基于当前课程仓库中文档的原文与关卡顺序文件整理;文中涉及的依赖版本(passport-local@~1.0.0passport@~0.4.1express-session@~1.17.1mongodb@~3.6.0bcrypt@~5.0.0)均以课程模板 package.json 中的声明为准。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
903
1.82 K
docsdocs
暂无描述
Markdown
888
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.51 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341