Express+JWT登录注册模块问题:中间件无法获取Token报错
问题分析与解决方案
核心错误点
- 变量作用域问题:中间件中
token变量定义在第一个try块内部,属于块级作用域,后续的try块无法访问该变量,直接导致ReferenceError: token is not defined。 - Token获取逻辑错误:
req.body || req.query || req.headers["x-access-token"]这种写法会返回整个req.body/req.query对象,而非其中的token字段,根本无法正确提取令牌。 - 表单跳转的Token传递缺失:普通HTML表单提交后执行
redirect,新发起的GET请求不会自动携带之前生成的JWT令牌,导致/home路由的中间件完全拿不到令牌。
分步修复代码
1. 修复JWT验证中间件(auth.js)
const jwt = require('jsonwebtoken'); const config = process.env; const verifyToken = (req, res, next) => { // 正确提取token:从body、query、headers的指定字段中获取 let token = req.body.token || req.query.token || req.headers["x-access-token"]; // 检查token是否存在 if (!token) { return res.status(403).send("未找到令牌,请重新登录"); } try { // 验证令牌并挂载到req对象 const decoded = jwt.verify(token, config.TOKEN_KEY); req.user = decoded; console.log(req.user); } catch (error) { return res.status(401).send(`令牌验证失败:${error.message}`); } return next(); }; module.exports = verifyToken;
2. 修复登录/注册接口的Token传递逻辑
普通HTML表单跳转无法自动携带Token,推荐将Token存入HTTP Cookie(无需前端额外处理),修改登录和注册接口:
注册接口修改
app.post("/register", async (req, res) => { try { const Username = req.body.Username; const email = req.body.email; const password = req.body.password; if (!(Username && email && password)) { return res.status(400).send("所有字段均为必填项!"); } const oldUser = await User.findOne({ email }); if (oldUser) { return res.status(409).send("用户已存在,请直接登录"); } const encryptedPassword = await bcrypt.hash(password, 10); const user = await User.create({ Username, email: email.toLowerCase(), password: encryptedPassword, }); const token = jwt.sign( { user_id: user._id, email }, process.env.TOKEN_KEY, { expiresIn: "2h" } ); user.token = token; await user.save(); // 将Token存入HttpOnly Cookie,防止XSS攻击 res.cookie('token', token, { httpOnly: true, maxAge: 2 * 60 * 60 * 1000, // 2小时有效期 secure: process.env.NODE_ENV === 'production' // 生产环境启用HTTPS }); res.status(201).redirect('/home'); } catch (error) { console.log(`注册错误:${error}`); res.status(500).send("服务器内部错误"); } })
登录接口修改
app.post("/login", async (req, res) => { try { const { email, password } = req.body; if (!(email && password)) { return res.status(400).send("请填写所有必填信息"); } const user = await User.findOne({ email }); if (user && (await bcrypt.compare(password, user.password))) { const token = jwt.sign( { user_id: user._id, email }, process.env.TOKEN_KEY, { expiresIn: "2h" } ); user.token = token; await user.save(); // 将Token存入HttpOnly Cookie res.cookie('token', token, { httpOnly: true, maxAge: 2 * 60 * 60 * 1000, secure: process.env.NODE_ENV === 'production' }); res.status(200).redirect('/home'); } else { res.status(400).send("用户不存在或密码错误,请先注册"); } } catch (error) { console.log(`登录错误:${error}`); res.status(500).send("服务器内部错误"); } })
3. 更新中间件的Token获取逻辑(支持从Cookie提取)
修改auth.js,增加从Cookie获取Token的逻辑:
const verifyToken = (req, res, next) => { // 新增从Cookie获取Token的方式 let token = req.body.token || req.query.token || req.headers["x-access-token"] || req.cookies.token; if (!token) { return res.status(403).send("未找到令牌,请重新登录"); } try { const decoded = jwt.verify(token, config.TOKEN_KEY); req.user = decoded; console.log(req.user); } catch (error) { return res.status(401).send(`令牌验证失败:${error.message}`); } return next(); };
4. 额外注意事项
- 确保你的Express应用已经安装并引入了
cookie-parser中间件,否则无法读取req.cookies:
在主文件中添加:npm install cookie-parserconst cookieParser = require('cookie-parser'); app.use(cookieParser()); - 主文件中你错误引入了
const req = require("express/lib/request");,这会覆盖Express的req对象,导致请求处理异常,必须删除该行。
内容的提问来源于stack exchange,提问作者AMITSINH VIKRAMSINH RAJPUT
相关产品推荐
相关产品推荐

