在签署 jwt 时,只保留 user_id 或整个用户对象是不是更好?

Posted

技术标签:

【中文标题】在签署 jwt 时,只保留 user_id 或整个用户对象是不是更好?【英文标题】:Is it better to keep just the user_id or the whole user object in the payload when signing a jwt?在签署 jwt 时,只保留 user_id 或整个用户对象是否更好? 【发布时间】:2021-01-30 11:19:15 【问题描述】:

我知道有两种方法。

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);

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

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

提前感谢您的回答:)

【问题讨论】:

第一种方法可能更有效,但最大的缺点是,如果您使用第一种方法并且用户更改了他们的姓名、电子邮件等,那么您将使用不正确的信息。我总是会进行额外的数据库调用以确保您拥有正确的数据 【参考方案1】:

使用 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,
                ,
              );
            
          );
        );
      );
    );
  );
);

【讨论】:

您的回答很有帮助,但不是很清楚。请使用示例详细说明,以便我可以将此答案标记为已接受。谢谢@K4R1。 @IbadShaikh 好的,添加了一个示例。希望它提供一些见解。我不是这些事情上最好的讲师/解释者。 例子中涉及到了MongoDB模型和bcrypt哈希,但是JWT部分明显存在。【参考方案2】:

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

_id 姓名 电子邮件 用户名

缺点案例:

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

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

app.post('/event', auth, (req, res) => 
   // creates an event with his old name.
   // Because the current payload contains old name.
)

现在如果用户注销然后再次登录,令牌将 在有效负载中包含用户的新名称。

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

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

谢谢

【讨论】:

以上是关于在签署 jwt 时,只保留 user_id 或整个用户对象是不是更好?的主要内容,如果未能解决你的问题,请参考以下文章

如果没有它可以解密,那么签署jwt有啥意义

使用 x509 证书签署 JWT 令牌时遇到问题

如何在整个会话中保留 JWT 令牌直到用户注销?

如何签署 JWT?

Pubsub 使用错误的密钥签署 JWT 令牌以进行推送

刷新令牌如何为 jwt 添加安全性?