签署 jwt 时,只保留 user_id 还是整个用户对象在有效载荷中更好?

Is it better to keep just the user_id or the whole user object in the payload when signing a jwt?

我知道有两种方法。

1) 传递整个用户对象:

let payload = {
    user: {
      id: user._id,
      name:user.name,
      email:user.email, 
      username:user.username
    },
  }

let token = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '1d' })

2) 仅传递 _id:

let token = jwt.sign({ id: this._id }, process.env.JWT_SECRET, {
    expiresIn: '1d',
});

在第二种方法中,每当我想访问受保护的路由时,我都必须使用存储在有效负载中的 _id 调用数据库来获取用户的详细信息。

// protect.js
// Verify token
const decoded = jwt.verify(token, process.env.JWT_SECRET);

req.user = await User.findById(decoded.id);

虽然在第一种方法中,我总是有一个完整的用户详细信息对象,而无需进行额外的数据库调用。

在阅读了一些博客之后,我开始知道我们应该保持我们的负载轻量级,但我心中的问题是,另一方面,我们必须在每次访问受保护的路由时进行单独的数据库调用. 我想知道哪种方法更有效。

提前感谢您的回答:)

使用 ID 签署 JWT 令牌,因为它在初始创建后不太可能更改。

用户名和电子邮件等其他数据很容易发生变化。

编辑:这是我不久前做的一个练习中的用户注册示例。希望对大家有所帮助。

// POST api/users => Register user
router.post("/", (req, res) => {
  const { name, email, password } = req.body;

  // Simple validation
  if (!name || !email || !password) {
    return res.status(400).json({ msg: "Please enter required information" });
  }

  // Check for existing user
  User.findOne({ email }).then((user) => {
    if (user) return res.status(400).json({ msg: "User already exists" });

    const newUser = new User({
      name,
      email,
      password,
    });

    // Create salt and Hash
    bcrypt.genSalt(10, (err, salt) => {
      bcrypt.hash(newUser.password, salt, (err, hash) => {
        if (err) throw err;
        newUser.password = hash;
        newUser.save().then((user) => {
          jwt.sign(
            { id: user.id },
            jwtSecret,
            { expiresIn: 3600 },
            (err, token) => {
              if (err) throw err;
              res.json({
                token,
                user: {
                  id: user.id,
                  name: user.name,
                  email: user.email,
                },
              });
            }
          );
        });
      });
    });
  });
});

假设您有一个包含整个用户对象的负载,包括:

  • _id
  • 名字
  • 电子邮件
  • 用户名

缺点案例:

  • 假设用户后来修改了自己的名字,就意味着他的数据 在数据库中得到更新,但他的有效载荷仍然过时 旧名称,因为没有调用数据库来获取更新的记录。

  • 如果用户现在尝试发出 POST 需要他的名字的请求 请求正文,然后过时的名称将被推送到 数据库而不是他的新名字。

    app.post('/event', auth, (req, res) => {
       // creates an event with his old name.
       // Because the current payload contains old name.
    })
    
  • 现在如果用户注销然后再次登录,令牌将 在负载中包含用户的新名称。

  • 并且每当使用用户的新更新名称获取所有事件时 保存在有效负载中,最近创建的最新事件 不会获取旧名称,因为它包含旧名称 用户名。

据我所知,这是我能给出的最好的解释。 分享您的想法!

谢谢