Node.js 中的授权与认证:完整指南
在现代 Web 开发中,用户数据安全是首要任务。在本指南中,我们将讲解在 Node.js 应用程序中实现认证(验证用户身份)和授权(验证访问权限)的关键工具和库。您将学习如何配置路由保护、对密码进行哈希处理以及使用访问令牌。
1. 基本概念
- 认证 — 验证用户是否为其所声称的身份的过程(通常通过登录名和密码)。
- 授权 — 确定经过认证的用户可以访问哪些资源和执行哪些操作。
- JWT(JSON Web Token) — 一种以 JSON 对象形式传输数据的紧凑且自包含的方式。用于创建访问令牌。
- 会话 — 在服务器上存储用户数据的传统方法(通常使用
express-session)。
2. Node.js 的关键库
2.1. bcrypt — 密码哈希处理
切勿以明文形式存储密码。bcrypt 是一个使用盐值进行安全哈希处理的库。
const bcrypt = require('bcrypt');const saltRounds = 10;
// 哈希密码async function hashPassword(password) { const salt = await bcrypt.genSalt(saltRounds); const hash = await bcrypt.hash(password, salt); return hash;}
// 验证密码async function checkPassword(password, hash) { const match = await bcrypt.compare(password, hash); return match; // true 或 false}2.2. jsonwebtoken — 使用 JWT
jsonwebtoken 库允许创建和验证 JWT 令牌。令牌通常由头部、载荷和签名组成。
const jwt = require('jsonwebtoken');const secretKey = '您的密钥';
// 创建令牌function generateToken(userId, role) { const payload = { userId, role }; const token = jwt.sign(payload, secretKey, { expiresIn: '1h' }); return token;}
// 验证令牌(中间件)function verifyToken(req, res, next) { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN if (!token) return res.status(401).json({ error: '未提供令牌' });
jwt.verify(token, secretKey, (err, decoded) => { if (err) return res.status(403).json({ error: '无效令牌' }); req.user = decoded; next(); });}2.3. Passport.js — 通用认证
Passport.js 是 Express 的中间件,支持超过 500 种策略(本地、OAuth、JWT、Google、Facebook 等)。本地策略示例:
const passport = require('passport');const LocalStrategy = require('passport-local').Strategy;const User = require('./models/User');
passport.use(new LocalStrategy( async (username, password, done) => { try { const user = await User.findOne({ username }); if (!user) return done(null, false, { message: '用户未找到' }); const isValid = await bcrypt.compare(password, user.password); if (!isValid) return done(null, false, { message: '密码错误' }); return done(null, user); } catch (err) { return done(err); } }));
// 序列化和反序列化(用于会话)passport.serializeUser((user, done) => done(null, user.id));passport.deserializeUser(async (id, done) => { const user = await User.findById(id); done(null, user);});3. 完整示例:注册和登录
将所有内容整合到一个简单的 Express 应用程序中。
const express =