You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express API中Admin添加分类报错:TypeError读取role属性未定义

问题排查与修复

错误 TypeError: Cannot read properties of undefined (reading 'role') 源于认证中间件的多个逻辑错误,以及权限中间件的属性访问错误,以下是具体问题和修复方案:

1. 核心错误点分析

(1) auth 中间件的基础错误

  • req.header.authorization 拼写错误:Express 中获取请求头的属性是复数 req.headers,而非单数 req.header
  • split("") 分隔符错误: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测试:

  1. 确保请求头中携带正确的 Authorization: Bearer <token>
  2. 调用添加分类接口,此时权限中间件能正确读取用户角色,验证通过后即可正常执行接口逻辑

内容的提问来源于stack exchange,提问作者jimzhou

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 14:03:23