【问题标题】:Understanding passport serialize deserialize了解护照序列化反序列化
【发布时间】:2015-02-22 14:43:34
【问题描述】:

您如何向外行解释 Passport 的序列化和反序列化方法的工作流程。

  1. 在调用passport.serializeUser 之后,user.id 去哪里?

  2. 我们在它之后调用passport.deserializeUser 它在工作流中的哪个位置?

    // used to serialize the user for the session
    passport.serializeUser(function(user, done) {
        done(null, user.id); 
       // where is this user.id going? Are we supposed to access this anywhere?
    });
    
    // used to deserialize the user
    passport.deserializeUser(function(id, done) {
        User.findById(id, function(err, user) {
            done(err, user);
        });
    });
    

我仍在努力解决它。我有一个完整的工作应用程序,没有遇到任何类型的错误。

我只是想了解这里到底发生了什么?

感谢任何帮助。

【问题讨论】:

    标签: node.js authentication express serialization passport.js


    【解决方案1】:

    Passport 使用serializeUser 函数将用户数据(在成功验证后)保存到会话中。函数deserializeUser用于从会话中检索用户数据。

    serializeUserdeserializeUser 函数都检查传递给它们的第一个参数,如果它是函数类型,serializeUserdeserializeUser 什么都不做,而是将这些函数放在一个函数堆栈中,这将被调用, 之后(当传递的第一个参数不是函数类型时)。 Passport 需要以下设置来在会话中进行身份验证后保存用户数据:

    app.use(session({ secret: "cats" }));
    app.use(passport.initialize());
    app.use(passport.session());
    

    使用的中间件的顺序很重要。重要的是要了解当新的授权请求开始时会发生什么:

    • 会话中间件创建会话(使用来自sessionStore 的数据)。

    • passport.initialize_passport 对象分配给请求对象,检查是否有会话对象,如果它存在,并且其中存在passport 字段(如果不存在 - 创建一个),将该对象分配给session _passport 中的字段。最后,它看起来像这样:

      req._passport.session = req.session['passport']
      

      所以,session 字段 references 对象,分配给 req.session.passport

    • passport.sessionreq._passport.session 中查找user 字段,如果找到,则将其传递给deserializeUser 函数并调用它。 deserializeUser 函数将req._passport.session.user 分配给请求对象的user 字段(如果在req._passport.session.user 中找到一个)。这就是为什么,如果我们像这样在serializeUser 函数中设置用户对象:

      passport.serializeUser(function(user, done) {
        done(null, JSON.strignify(user)); 
      });
      

      然后我们需要解析它,因为它在user字段中保存为JSON

       passport.deserializeUser(function(id, done) {
         // parsed user object will be set to request object field `user`
         done(err, JSON.parse(user));
       });
      

    所以,deserializeUser 函数首先调用,当你设置 Passport 时,将你的回调放入 _deserializers 函数堆栈。第二次,将在passport.session 中间件中调用,将user 字段分配给请求对象。这也会在分配user 字段之前触发我们的回调(我们放入passport.deserializeUser())。

    serializeUser 函数首先调用,当您设置 Passport 时(类似于 deserializeUser 函数),但它将用于序列化用户对象以保存在会话中。第二次,将在login/logIn (alias) 方法中调用,通过Passport 附加,用于保存会话中的用户对象。 serializeUser 函数还检查 _serializers 堆栈是否已推送到它的函数(其中一个已添加,当我们设置 Passport 时):

    passport.serializeUser(function(user, done) ...
    

    并调用它们,然后将用户对象(字符化)或用户 ID 分配给 req._passport.session.user。重要的是要记住session 字段直接引用req.session 对象中的passport 字段。这样,用户在会话中保存(因为req._passport.session 引用对象req.session.passport,并且req._passport.session 在每个传入请求中由passport.initialize 中间件修改)。 当请求结束时,req.session 的数据将存储在sessionStore 中。

    授权成功后,第二个请求开始时会发生什么:

    • session 中间件从 sessionStore 获取会话,我们的用户数据已经保存在其中
    • passport.initialize 检查是否有会话并将req.session.passport 分配给req._passport.session
    • passport.session 检查 req._passport.session.user 并反序列化它。在这个阶段(如果req._passport.session.user 是真实的),我们将有req.userreq.isAuthenticated() 返回true

    【讨论】:

      【解决方案2】:
      1. 在调用passport.serializeUser 之后,user.id 去哪里?

      用户 ID(您作为 done 函数的第二个参数提供)保存在会话中,稍后用于通过 deserializeUser 函数检索整个对象。

      serializeUser 确定用户对象的哪些数据应该存储在会话中。 serializeUser 方法的结果以req.session.passport.user = {} 的形式附加到会话中。例如,这里将是(因为我们提供用户 ID 作为键)req.session.passport.user = {id: 'xyz'}

      1. 我们在它之后调用 passport.deserializeUser 它在工作流中的哪个位置?

      deserializeUser 的第一个参数对应于分配给done 函数的用户对象的键(参见 1.)。因此,您的整个对象都是在该键的帮助下检索的。这里的键是用户 ID(键可以是用户对象的任何键,即姓名、电子邮件等)。 在deserializeUser 中,该键与内存数组/数据库或任何数据资源匹配。

      获取的对象作为req.user附加到请求对象

      视觉流

      passport.serializeUser(function(user, done) {
          done(null, user.id);
      });              │
                       │ 
                       │
                       └─────────────────┬──→ saved to session
                                         │    req.session.passport.user = {id: '..'}
                                         │
                                         ↓           
      passport.deserializeUser(function(id, done) {
                         ┌───────────────┘
                         │
                         ↓ 
          User.findById(id, function(err, user) {
              done(err, user);
          });            └──────────────→ user object attaches to the request as req.user   
      });
      

      【讨论】:

      • 所以user.id 保存为req.session.passport.user 还是user 本身保存为req.session.passport.user
      • @A.B 我不明白你对 uzay95 的建议。所以在我的会话中,我只有 user._id。但是对于每个请求,我都必须使用该 ID 从数据库中反序列化,即 findUserByID,然后将其放入 req.user。如何避免对每个请求进行此类调用?
      • @Zanko 您可以将整个用户对象放入会话数据中,但这通常不是一个好主意,因为它可能会产生其他副作用。例如,当用户更新他/她的用户名时,您也必须更新会话数据,否则您将因为“重命名功能损坏”而获得票证。这是一个相对无害的例子。权限位或同等敏感数据(哎呀...)也可能发生同样的情况。如果你有重复的数据,基本上你总是会遇到同样的问题。 TL;DR - 不要这样做。
      • @Valamorde Done 是一个由护照内部管理的回调函数,它会根据参数将您带到下一步,成功/失败等。认为它类似于调用 next 以便于理解
      • 如果我没记错的话,图中的req.session.passport.user = {id: '..'} 部分有点偏离,应该是req.session.passport.user = 785352,其中785352user.id。我无法通过控制台记录来证明这一点,但它似乎是有道理的。当您调用done(null, user.id); 时,将第二个参数(在这种情况下为user.id)分配给req.session.passport.user,而不是分配给req.session.passport.user.id 是有意义的。因为如果你改为传递user 会怎样? req.sesssion.passport.user.id = user 没有意义。
      【解决方案3】:

      对于使用 Koa 和 koa-passport 的任何人:

      知道在 serializeUser 方法中设置的用户密钥(通常是该用户的唯一 ID)将存储在:

      this.session.passport.user

      当您在 deserializeUser 中设置 done(null, user) 时,其中 'user' 是数据库中的某个用户对象:

      this.req.user 要么 this.passport.user

      由于某种原因this.user Koa 上下文永远不会在你的 deserializeUser 方法中调用 done(null, user) 时设置。

      因此您可以在调用 app.use(passport.session()) 后编写自己的中间件,将其放入 this.user 中,如下所示:

      app.use(function * setUserInContext (next) {
        this.user = this.req.user
        yield next
      })
      

      如果您不清楚 serializeUser 和 deserializeUser 的工作原理,请在推特上联系我。 @yvanscher

      【讨论】:

      猜你喜欢
      • 2016-04-13
      • 2019-12-09
      • 2016-07-03
      • 1970-01-01
      • 2014-11-27
      • 1970-01-01
      • 2020-09-08
      • 2018-03-03
      • 2015-12-22
      相关资源
      最近更新 更多