如何在Node.js与Express中创建API key系统并应用于受保护路由
背景说明
我使用Node.js、Express.js、MongoDB搭建了后端系统,接入了JSON Web Token实现用户登录时生成令牌的鉴权能力,可通过令牌访问受保护路由。
当前问题
需要实现一套API Key系统并将其应用到受保护路由,目前使用Postman测试接口。
已实现的API Key校验中间件
目前已经编写了如下中间件,用于访问路由前校验API Key:
const protect = asyncHandler(async (req, res, next) => { if (req.header("api-key")) { try { //let api_key = req.query.api_key; // 版本1:通过查询参数传递 //let api_key = req.params.apikey; // 版本2:通过URL路径参数传递 let api_key = req.header("api-key"); // 版本3:通过请求头传递 console.log(api_key); req.user = await User.findOne({ apiKey: api_key, host: process.env.HOST, }).select("-password"); if (req.user) { next(); } else { res.status(401); throw new Error("未授权,API Key无效"); } } catch (error) { res.status(401); throw new Error("未授权,API Key校验失败"); } } else { res.status(401); throw new Error("未授权,未携带API Key"); } });
完整API Key系统实现方案
1. 新增API Key生成逻辑
在用户注册或用户主动申请API Key的接口中,添加生成唯一API Key的逻辑,代码示例如下:
// 引入crypto模块生成随机字符串 const crypto = require('crypto'); // 用户申请API Key的接口逻辑 const generateApiKey = asyncHandler(async (req, res) => { // 生成32位随机字符串作为API Key const apiKey = crypto.randomBytes(16).toString('hex'); // 存入对应用户的文档中,同时记录绑定的host(如果有跨域限制需求) const user = await User.findByIdAndUpdate( req.user._id, { apiKey: apiKey, host: process.env.HOST }, { new: true } ).select('-password'); res.json({ apiKey: user.apiKey }); })
2. 中间件兼容双鉴权逻辑
如果需要同时支持JWT和API Key两种鉴权方式,可以修改现有中间件,优先匹配其中一种鉴权方式即可:
const protect = asyncHandler(async (req, res, next) => { let token; // 先判断有没有API Key if (req.header("api-key")) { try { const api_key = req.header("api-key"); req.user = await User.findOne({ apiKey: api_key, host: process.env.HOST, }).select("-password"); if (req.user) return next(); res.status(401); throw new Error("API Key无效"); } catch (error) { res.status(401); throw new Error("API Key校验失败"); } } // 没有API Key的话判断有没有JWT令牌 else if (req.headers.authorization?.startsWith('Bearer')) { try { token = req.headers.authorization.split(' ')[1]; const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = await User.findById(decoded.id).select('-password'); next(); } catch (error) { res.status(401); throw new Error("令牌无效"); } } else { res.status(401); throw new Error("未携带鉴权信息"); } });
3. 路由绑定中间件
将编写好的中间件绑定到需要受保护的路由即可:
// 示例:获取用户信息接口需要鉴权 router.get('/user/profile', protect, getUserProfile)
4. Postman测试注意事项
在Postman的请求Header中添加api-key字段,值为用户生成的API Key即可完成测试,不需要额外配置其他参数。
注意:API Key属于敏感信息,请勿在前端代码中硬编码,传输时必须使用HTTPS协议避免泄露,可定期更换API Key提升安全性。
内容的提问来源于stack exchange,提问作者user2247764
相关产品推荐
相关产品推荐

