Express API中Admin添加分类报错:TypeError读取role属性未定义
问题排查与修复
错误 TypeError: Cannot read properties of undefined (reading 'role') 源于认证中间件的多个逻辑错误,以及权限中间件的属性访问错误,以下是具体问题和修复方案:
1. 核心错误点分析
(1) auth 中间件的基础错误
req.header.authorization拼写错误:Express 中获取请求头的属性是复数req.headers,而非单数req.headersplit("")分隔符错误:Bearer Token 的格式是Bearer <token>,应该用空格split(" ")拆分,而非空字符串- 变量大小写不一致:中间件中挂载的是
req.UserId(大写U),但权限中间件中访问的是req.userId(小写u) - 未正确传递用户角色:Token 中已包含
role信息,但未挂载到req对象,导致权限中间件无法读取
(2) adminMiddleware 的逻辑错误
- 错误访问属性:
req.userId是用户ID字符串,而非用户对象,直接访问req.userId.role必然返回undefined - 逻辑判断错误:
!req.userId.role === "admin"的逻辑完全混乱,正确的判断应该是检查用户角色是否为admin
(3) 认证控制器的次要逻辑错误
- 登录/注册中的条件判断错误:
!existingUser.role === "admin"和!password == confirmPassword的逻辑不符合预期,会导致判断失效
2. 修复后的完整代码
修复后的 auth 中间件
const jwt = require("jsonwebtoken"); const User = require("../models/user"); exports.auth = async (req, res, next) => { try { // 验证Authorization请求头是否存在 const authHeader = req.headers.authorization; if (!authHeader) { return res.status(401).json({ message: "Authorization required" }); } // 拆分Bearer Token,获取token部分 const token = authHeader.split(" ")[1]; const isCustomAuth = token.length < 500; let decodeData; if (token && isCustomAuth) { // 验证自定义JWT Token decodeData = jwt.verify(token, process.env.JWT_SECRET); // 统一变量名,同时挂载用户ID和角色到req对象 req.userId = decodeData.id; req.userRole = decodeData.role; // 可选:从数据库二次验证用户是否存在(增强安全性) // const user = await User.findById(decodeData.id); // if (!user) { // return res.status(401).json({ message: "User not found" }); // } // req.user = user; } else { // 第三方登录Token处理(根据你的业务场景可选) decodeData = jwt.decode(token); req.userId = decodeData.sub; } } catch (error) { console.log(error); return res.status(401).json({ message: "Invalid or expired token" }); } next(); };
修复后的 adminMiddleware
exports.adminMiddleware = (req, res, next) => { // 方式1:直接使用Token中解析的角色 if (req.userRole !== "admin") { return res.status(403).json({ message: "Access denied: Admin only" }); } // 方式2:如果从数据库查询了用户对象,使用以下逻辑 // if (!req.user || req.user.role !== "admin") { // return res.status(403).json({ message: "Access denied: Admin only" }); // } next(); };
修复后的认证控制器(登录/注册)
const User = require("../models/user"); const jwt = require("jsonwebtoken"); const bcrypt = require("bcrypt"); exports.loginUser = async (req, res) => { const { email, password } = req.body; try { const existingUser = await User.findOne({ email }); if (!existingUser) { return res.status(400).json({ message: "User does not exist." }); } // 修复角色判断逻辑 if (existingUser.role !== "admin") { return res.status(400).json({ message: "User is not admin." }); } const isPasswordCorrect = await bcrypt.compare( password, existingUser.password ); if (!isPasswordCorrect) return res.status(400).json({ message: "Invalid credentials." }); const token = jwt.sign( { email: existingUser.email, id: existingUser._id, role: existingUser.role, }, process.env.JWT_SECRET, { expiresIn: "3d" } ); res.status(200).json({ result: existingUser, token }); } catch (error) { console.log(error); } }; exports.signupUser = async (req, res) => { const { firstName, lastName, email, password, confirmPassword } = req.body; try { const existingUser = await User.findOne({ email }); if (existingUser) return res.status(400).json({ message: "Admin already exists." }); // 修复密码匹配判断逻辑 if (password !== confirmPassword) return res.status(400).json({ message: "Passwords don't match" }); const hashedPassword = await bcrypt.hash(password, 12); const result = await User.create({ email, password: hashedPassword, firstName, lastName, role: "admin", }); const token = jwt.sign( { email: result.email, id: result._id, role: result.role }, process.env.JWT_SECRET, { expiresIn: "3d" } ); res.status(200).json({ result, token }); } catch (error) { console.log(error); } };
3. 测试验证
修复后,重新用Postman测试:
- 确保请求头中携带正确的
Authorization: Bearer <token> - 调用添加分类接口,此时权限中间件能正确读取用户角色,验证通过后即可正常执行接口逻辑
内容的提问来源于stack exchange,提问作者jimzhou
相关产品推荐
相关产品推荐

