在现代API开发中,用户认证是保障数据安全与访问控制的核心环节。对于许多从前端开发转向全栈的开发者而言,构建一套清晰、健壮且易于维护的认证体系是项目成功的关键。本文将基于真实项目,详细拆解在Node.js的Express框架中集成JWT(JSON Web Token)认证的完整实践,提供可复用的模板代码。
一、JWT核心概念解析
什么是JWT?
JWT是一种开放标准,用于在双方之间安全地传输信息。一个完整的JWT由三部分组成,以点号.分隔:Header.Payload.Signature。
三个组成部分
- Header(头部):包含令牌类型(如JWT)和签名算法(如HS256)。
- Payload(载荷):存放需要传递的声明信息,如用户ID、角色。标准声明包括过期时间(
exp)、签发者(iss)等。请注意,Payload内容仅是Base64编码,可以被解码,切勿存放密码等敏感信息。
- Signature(签名):由编码后的Header、Payload以及一个密钥(
secret)通过指定算法生成,用于验证令牌在传输过程中是否被篡改。
JWT在副业项目中的优势
- 无状态:服务端无需存储会话,减轻了服务器负担。
- 扩展性好:天然支持移动端、Web端等多端登录。
- 部署友好:与现代运维及容器化部署方案兼容性高。
二、项目集成准备
1. 安装依赖
首先,在项目根目录下安装jsonwebtoken库。
cd server
npm install jsonwebtoken
2. 环境变量配置
将密钥等敏感配置置于环境变量中,便于不同环境的管理。
# .env 文件
JWT_SECRET=your_super_secret_key_here
JWT_EXPIRES_IN=1h
JWT_SECRET:用于签名和验证Token的密钥,务必保证其复杂性与私密性。
JWT_EXPIRES_IN:定义Token的有效期,例如1h(1小时)、7d(7天)。
3. 统一定义业务错误码
在config/codes.js等配置文件中,明确定义与认证相关的错误码,便于前后端协同。
// config/codes.js
module.exports = {
UNAUTHORIZED: 401, // 未授权/未登录
TOKEN_EXPIRED: 402, // Token过期
// ... 其他错误码
};
三、登录流程:颁发Token
接口说明
- 路径:
POST /api/login
- 功能: 验证用户凭证,成功后颁发JWT。
控制器实现 (authController.js)
const jwt = require('jsonwebtoken');
const codes = require('../config/codes');
const logger = require('../config/logger');
const login = (req, res) => {
const { username, password } = req.body;
// 1. 校验参数
if (!username || !password) {
return res.cc(null, '用户名或密码不能为空', codes.INVALID_PARAMS);
}
// 2. 校验用户凭证 (此处为示例,真实项目请查询数据库)
if (username === 'testuser' && password === 'testpass') {
const user = { id: 1, username: 'testuser', role: 'admin' };
// 3. 生成JWT Token
const token = jwt.sign(user, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRES_IN,
});
logger.info(`用户 ${username} 登录成功,颁发 Token`);
// 4. 返回Token及用户信息
return res.cc(
{
token,
user: { id: user.id, username: user.username, role: user.role },
},
'登录成功'
);
}
logger.warn(`用户 ${username} 登录失败:账号或密码错误`);
return res.cc(null, '用户名或密码错误', codes.UNAUTHORIZED);
};
module.exports = { login };
四、鉴权流程:校验Token
认证中间件实现
创建一个通用的auth中间件,用于保护需要登录态的路由。
// middleware/auth.js
const jwt = require('jsonwebtoken');
const codes = require('../config/codes');
const logger = require('../config/logger');
const auth = (req, res, next) => {
// 1. 从请求头获取Token
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
logger.warn('未提供或格式错误的 Authorization Header');
return res.cc(null, '访问未经授权,请提供 Token', codes.UNAUTHORIZED);
}
const token = authHeader.split(' ')[1]; // 提取真正的Token
try {
// 2. 验证并解码Token
const decoded = jwt.verify(token, process.env.JWT_SECRET);
// 3. 将用户信息挂载到请求对象上,供后续中间件或控制器使用
req.user = decoded;
logger.info(`Token 校验成功,用户ID:${decoded.id}`);
next(); // 验证通过,继续执行下一个中间件或路由处理函数
} catch (err) {
// 4. 处理验证失败(过期或无效)
if (err.name === 'TokenExpiredError') {
return res.cc(null, 'Token 已过期,请重新登录', codes.TOKEN_EXPIRED);
}
logger.error(`Token 校验失败:${err.message}`);
return res.cc(null, '无效 Token,请重新登录', codes.UNAUTHORIZED);
}
};
module.exports = auth;
五、在路由中应用认证
在路由文件中,只需在需要保护的路由上引入auth中间件即可。
// routes/auth.js
const express = require('express');
const router = express.Router();
const { login } = require('../controllers/authController');
const auth = require('../middleware/auth');
const asyncHandler = require('../utils/asyncHandler');
// 公开路由:登录
router.post('/login', login);
// 受保护路由:需要有效Token才能访问
router.get(
'/protected',
auth, // 应用认证中间件
asyncHandler(async (req, res) => {
res.cc(
{
user: req.user, // 可以直接从req.user获取登录用户信息
message: `欢迎你,用户ID:${req.user.id}`,
},
'访问受保护资源成功'
);
})
);
module.exports = router;
六、接口测试方法
1. 获取Token
使用POST请求调用登录接口。
POST /api/login
Content-Type: application/json
{
"username": "testuser",
"password": "testpass"
}
成功响应将包含token字段。
2. 访问受保护接口
在请求头中携带获取到的Token,访问受保护的接口。
GET /api/protected
Authorization: Bearer <your_token_here>
七、总结
通过上述步骤,我们构建了一套完整的Express JWT认证与授权体系。这套方案不仅实现了基础的登录鉴权,更提供了清晰的错误处理、日志记录和可扩展的用户信息管理,为后续添加角色权限控制(RBAC)等功能奠定了坚实的基础。其无状态特性、对前端友好的交互方式以及规范的代码结构,使其成为个人项目或中小型应用理想的认证解决方案。