在 Node.js 应用开发里,用户身份识别是最基础也最容易出错的环节。如果把用户 ID 交由前端传递并直接信任,就给了攻击者伪造身份的空间。我们应当在服务端完成身份校验,并把当前用户 ID 安全地附着在请求生命周期中。

为什么不能让前端传用户 ID
一些初学者会在登录后把用户 ID 存到浏览器的 localStorage 或隐藏表单里,之后每个请求都带上这个 ID。这种做法的问题是:前端数据完全可控,攻击者只需修改请求体里的 user_id 就能冒充他人。
- 隐藏字段可被浏览器开发者工具修改
- 本地存储中的值能被脚本任意读写
- 接口若直接信任该值,便产生越权漏洞
使用会话(Session)传递用户 ID
在 Express 中,登录成功后将用户 ID 存入服务端会话,之后通过中间件把 req.session.userId 挂到请求上。业务代码只读取服务端的值。
const express = require('express');
const session = require('express-session');
const app = express();
app.use(session({
secret: 'safe_secret_key',
resave: false,
saveUninitialized: false
}));
// 登录时写入会话
app.post('/login', (req, res) => {
// 假设已校验账号密码,得到用户对象 user
const user = { id: 1, name: 'test' };
req.session.userId = user.id;
res.json({ ok: true });
});
// 中间件保证后续请求能拿到真实 ID
function attachUser(req, res, next) {
if (!req.session.userId) {
return res.status(401).json({ msg: '未登录' });
}
req.currentUserId = req.session.userId;
next();
}
app.get('/profile', attachUser, (req, res) => {
// 只使用服务端会话中的 ID
res.json({ userId: req.currentUserId });
});
app.listen(3000);
使用 JWT 在接口间传递
前后端分离时常用 JWT。登录后签发包含用户 ID 的 token,前端只负责携带,服务端验证签名后解析出 ID。
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
const SECRET = 'server_side_secret';
app.post('/login', (req, res) => {
const user = { id: 2, name: 'tom' };
const token = jwt.sign({ uid: user.id }, SECRET, { expiresIn: '2h' });
res.json({ token });
});
function auth(req, res, next) {
const authHeader = req.headers['authorization'] || '';
const token = authHeader.split(' ')[1];
if (!token) return res.sendStatus(401);
try {
const payload = jwt.verify(token, SECRET);
req.currentUserId = payload.uid;
next();
} catch (e) {
res.sendStatus(403);
}
}
app.get('/order', auth, (req, res) => {
res.json({ operator: req.currentUserId });
});
app.listen(3000);
常见注意事项
| 做法 | 风险 |
|---|---|
| 前端传 user_id 参数 | 可被篡改导致越权 |
| 服务端会话存 ID | 安全,需保护 session 密钥 |
| JWT 携带 uid | 安全,密钥不能泄露 |
无论采用哪种方式,核心原则都是:当前用户 ID 必须由服务端在认证后生成并校验,前端只传输凭证,不直接传输身份值。这样即可避免前端注入用户 ID 的风险。