如何保障React+Express架构下客户端HTTP接口调用的安全性?
防止无关人员访问Express API的最佳方案
你之前直接在前端携带API密钥的方式确实不可行,因为前端代码和请求头都能被轻易查看。下面是几种适合你场景的可靠方案:
1. 用户身份认证(核心方案)
通过登录机制识别合法用户,只有认证通过的用户才能访问API,最常用的是JWT(JSON Web Token):
客户端实现(React + Axios)
登录成功后将后端返回的token存在内存或安全的存储中,后续请求自动携带:
// 登录示例 async function login(username, password) { const res = await axios.post(`${backendDomain}/login`, { username, password }); // 将token存在内存(比如React Context或状态管理工具),避免存localStorage防止XSS window.__authToken = res.data.token; } // 请求API时携带token async function getData(id){ try { const res = await axios.get(`${backendDomain}/data/${id}`, { headers: { 'Authorization': `Bearer ${window.__authToken}` } }); return res.data; } catch (error) { // 处理认证失败,比如跳转登录页 } }
服务器端实现(Express)
添加JWT验证中间件,只有token合法才允许访问API:
const jwt = require('jsonwebtoken'); const secretKey = process.env.JWT_SECRET; // 私钥存在环境变量,不要硬编码 // JWT验证中间件 function authenticateToken(req, res, next) { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; if (!token) return res.sendStatus(401); jwt.verify(token, secretKey, (err, user) => { if (err) return res.sendStatus(403); req.user = user; next(); }); } // 登录接口,生成token app.post('/login', (req, res) => { // 验证用户名密码逻辑(比如查数据库) const validUser = checkUser(req.body.username, req.body.password); if (!validUser) return res.sendStatus(401); const token = jwt.sign({ userId: validUser.id }, secretKey, { expiresIn: '1h' }); res.json({ token }); }); // 受保护的API,添加验证中间件 app.get('/data/:id', authenticateToken, function(req, res) { // 这里可以用req.user获取用户信息,返回该用户有权限的数据 const data = fetchData(req.params.id, req.user.userId); res.send(data); });
2. 会话验证(适合传统场景)
如果不想用JWT,可以用Express会话机制,依赖浏览器的cookie(需配置HttpOnly和Secure属性防止XSS):
服务器端配置
const session = require('express-session'); const cookieParser = require('cookie-parser'); app.use(cookieParser()); app.use(session({ secret: process.env.SESSION_SECRET, // 会话密钥存在环境变量 resave: false, saveUninitialized: false, cookie: { httpOnly: true, // 禁止前端JS访问cookie secure: process.env.NODE_ENV === 'production', // 生产环境仅HTTPS传输 maxAge: 3600000 // 1小时有效期 } })); // 登录接口 app.post('/login', (req, res) => { const validUser = checkUser(req.body.username, req.body.password); if (!validUser) return res.sendStatus(401); req.session.userId = validUser.id; res.sendStatus(200); }); // 验证会话的中间件 function checkSession(req, res, next) { if (!req.session.userId) return res.sendStatus(401); next(); } // 受保护的API app.get('/data/:id', checkSession, function(req, res) { const data = fetchData(req.params.id, req.session.userId); res.send(data); });
客户端无需额外处理cookie,浏览器会自动在请求中携带会话cookie。
3. 请求签名(适合敏感操作)
对于需要更高安全性的接口,可以结合认证+请求签名,防止请求被篡改或重放:
- 客户端登录后,后端返回JWT和临时签名密钥(仅存在内存)
- 每次请求时,用密钥对请求路径、参数、当前时间戳做HMAC-SHA256签名,放在
X-Signature头 - 服务器验证签名的正确性,同时检查时间戳是否在有效期内(比如5分钟),防止重放攻击
4. 辅助防护措施
严格CORS配置
限制只有你的前端域名能跨域请求API,防止其他网站恶意调用:
const cors = require('cors'); app.use(cors({ origin: 'https://你的前端域名.com', // 只允许你的前端域名 credentials: true // 允许携带cookie或token }));
注意:CORS是浏览器层面的限制,无法阻止Postman等工具直接调用,必须结合认证方案。
速率限制
用express-rate-limit防止暴力请求:
const rateLimit = require('express-rate-limit'); const apiLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15分钟 max: 100 // 每个IP最多100次请求 }); app.use('/data/', apiLimiter);
强制HTTPS
生产环境必须启用HTTPS,防止请求被窃听或篡改。
内容的提问来源于stack exchange,提问作者RoyBenAviv
相关产品推荐
相关产品推荐

