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

Mongoose Schema Enum属性不生效问题排查求助

问题:用户注册时指定的role字段未生效,始终被设为默认值user

在开发用户模型userModel时,我为userSchema中的role字段设置了enum属性,可选值为['user', 'guide', 'lead-guide', 'admin'],默认值为user。但在Postman测试/signup路由时,无论请求体中传入的role是哪个有效值,最终创建的用户角色始终是默认的user。


相关代码片段

const mongoose = require('mongoose');
const validator = require('validator');
const bycrypt = require('bcryptjs');

const userSchema = new mongoose.Schema({
  name: {
    type: String,
    required: [true, '用户必须填写姓名'],
  },
  email: {
    type: String,
    required: [true, '用户必须填写邮箱'],
    unique: true,
    lowercase: true,
    validate: [validator.isEmail, '请提供有效的邮箱地址'],
  },
  photo: {
    type: String,
  },
  role: {
    type: String,
    enum: ['user', 'guide', 'lead-guide', 'admin'],
    default: 'user',
  },
  password: {
    type: String,
    required: [true, '请设置密码'],
    minlength: 8,
    select: false, // 查询时隐藏该字段
  },
  passwordConfirm: {
    type: String,
    required: [true, '请确认密码'],
    validate: {
      // 仅在CREATE和SAVE操作时生效(更新操作不触发)
      validator: function (el) {
        return el === this.password;
      },
      message: '密码与确认密码不一致!',
    },
  },
  passwordChangedAt: Date,
});

// 密码加密中间件
userSchema.pre('save', async function (next) {
  // 仅当密码被修改时执行加密逻辑
  if (!this.isModified('password')) return next();

  // 使用bcrypt哈希密码,cost值设为12
  this.password = await bycrypt.hash(this.password, 12);
  // 清除确认密码字段(仅用于输入验证,无需存入数据库)
  this.passwordConfirm = undefined;

  next();
});

// 密码验证实例方法(用于登录时校验密码)
userSchema.methods.correctPassword = async function (
  candidatePassword,
  userPassword,
) {
  // 返回密码比对结果(true/false)
  return bycrypt.compare(candidatePassword, userPassword);
};

// 密码修改时间校验方法(用于JWT令牌有效性判断)
userSchema.methods.changedPasswordAfter = function (JWTTimestamp) {
  if (this.passwordChangedAt) {
    // 将时间戳转换为秒级
    const changedTimeStamp = parseInt(
      this.passwordChangedAt.getTime() / 1000,
      10,
    );
    console.log(changedTimeStamp, JWTTimestamp);
    // 如果令牌生成时间早于密码修改时间,说明密码已更新
    return JWTTimestamp < changedTimeStamp;
  }
  // 返回false表示密码未修改
  return false;
};

// 创建User模型
const User = mongoose.model('User', userSchema);
module.exports = User;

Postman请求体

{
    "name":"Admin10",
    "email":"Admisnl13'24s0@gmail.com",
    "password":"pass1234",
    "passwordConfirm":"pass1234",
    "role":"admin"   
}

实际响应结果

{
    "status": "success",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY1NWYzMDBjZWJhOTQxMzM2MDA2YjFiNSIsImlhdCI6MTcwMDczNzAzNywiZXhwIjoxNzA4NTEzMDM3fQ.a474i9cMNIkeTkq4wA97PXQzhspfGzGQVc4expRM5mM",
    "data": {
        "user": {
            "role": "user",
            "_id": "655f300ceba941336006b1b5",
            "name": "Admin10",
            "email": "admisnl13'24s0@gmail.com",
            "password": "$2a$12$S8RCXsoAj2K6zZZgvLFXh.z1rrL0WoABnrg6NzUwRx2Dq2RsuqTQG",
            "__v": 0
        }
    }
}

问题排查与解决方案

核心原因:路由逻辑未传递role字段到用户创建流程

你的/signup路由处理代码大概率遗漏了role字段的传递,导致mongoose无法获取到请求体中的role值,只能使用默认值。

解决方法1:明确传递role字段

在创建用户时,确保解构请求体时包含role,并传入User.create()方法:

// 正确示例:解构包含role字段
const { name, email, password, passwordConfirm, role } = req.body;
const user = await User.create({
  name,
  email,
  password,
  passwordConfirm,
  role // 必须显式传入该字段
});

解决方法2:使用扩展运算符传递请求体(需配合参数校验)

如果需要传递所有合法字段,可以使用扩展运算符,但建议结合express-validator等中间件做参数校验,防止恶意字段注入:

// 配合校验中间件的示例
const user = await User.create({ ...req.body });

安全补充:限制高权限角色的设置

为了避免普通用户注册时设置admin等高权限角色,建议在创建用户前添加逻辑限制:

const userData = { ...req.body };
// 仅允许内部请求或特定权限的用户设置高权限角色
if (!req.isInternalRequest) {
  userData.role = 'user';
}
const user = await User.create(userData);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 21:47:08