JavaScript (Node.js) 中的授权与身份验证
在现代 Web 开发中,用户数据安全是首要任务。在本指南中,我们将解析 Node.js 环境中 身份验证(验证身份)和 授权(访问控制)的关键概念。您将学习如何实现注册、密码登录、使用 JWT 令牌以及保护路由。
1. 基本概念
- 身份验证 — 确认身份的过程(例如,通过邮箱+密码)。
- 授权 — 检查用户是否有权执行操作(admin/user 角色)。
- JWT(JSON Web Token) — 一种以令牌形式传输数据的紧凑方式,常用于会话管理。
- 密码哈希 — 强制性的安全要求(bcrypt, argon2)。
2. 安装依赖
我们需要以下 npm 包:
npm install express bcryptjs jsonwebtoken dotenvexpress— Web 框架bcryptjs— 密码哈希jsonwebtoken— 创建和验证 JWTdotenv— 管理密钥
3. 简单注册与密码哈希
创建一个基本的用户模型(示例中使用内存数组,实际项目中请替换为数据库):
const bcrypt = require('bcryptjs');const users = []; // 模拟数据库
async function registerUser(email, password) { const salt = await bcrypt.genSalt(10); const hashedPassword = await bcrypt.hash(password, salt); users.push({ email, password: hashedPassword }); console.log('用户已注册:', email);}
// 调用示例:registerUser('user@example.com', 'myPassword123');4. 使用 JWT 进行身份验证
成功登录后,我们向客户端颁发一个 JWT 令牌。令牌包含用户 ID 和过期时间。
const jwt = require('jsonwebtoken');require('dotenv').config();
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
function loginUser(email, password) { const user = users.find(u => u.email === email); if (!user) return null;
const isValid = bcrypt.compareSync(password, user.password); if (!isValid) return null;
// 创建令牌 const token = jwt.sign( { userId: user.email, role: 'user' }, JWT_SECRET, { expiresIn: '1h' } ); return token;}5. 用于保护路由的中间件
创建一个中间件,用于检查 Authorization 头中的 JWT:
function authMiddleware(req, res, next) { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) { return res.status(401).json({ message: '未提供令牌' }); }
try { const decoded = jwt.verify(token, JWT_SECRET); req.user = decoded; // 将用户数据传递给请求 next(); } catch (err) { return res.status(403).json({ message: '无效的令牌' }); }}6. 在 Express 中的使用示例
将所有内容整合在一起——创建登录路由和受保护的个人资料路由:
const express = require('express');const app = express();app.use(express.json());
// 登录路由app.post('/api/login', (req, res) => { const { email, password } = req.body; const token = loginUser(email, password); if (!token) { return res.status(401).json({ message: '凭据无效' }); } res.json({ token });});
// 受保护的路由app.get('/api/profile', authMiddleware, (req, res) => { res.json({ message: '访问