使用passport.js进行身份验证时,语法混乱?

4
有几个问题我不明白在passport.js中。
1.

var passport = require('passport')
  , LocalStrategy = require('passport-local').Strategy;

passport.use(new LocalStrategy(
  function(username, password, done) {
    User.findOne({ username: username }, function (err, user) {
      if (err) { return done(err); }
      if (!user) {
        return done(null, false, { message: 'Incorrect username.' });
      }
      if (!user.validPassword(password)) {
        return done(null, false, { message: 'Incorrect password.' });
      }
      return done(null, user);
    });
  }
));

nulldone()函数中代表什么?它似乎总是作为第一个参数出现,我对它的实际作用感到困惑。

2.

passport.serializeUser(function(user, cb) {
  cb(null, user);
});

passport.deserializeUser(function(obj, cb) {
  cb(null, obj);
});

serialize和deserialize是什么?当会话存储在浏览器中时,登录后是否会调用serialize?当访问页面时,反序列化会话以验证用户,这时会调用deserialize吗?

最后,在cb(null, user);中,null参数是什么意思?


1
在nodejs中有一个惯例,即将任何错误作为回调的第一个参数传递。您会在几乎所有的node库中看到这一点,包括像fs这样的内置模块。因此,传递null就像是在将结果传递给回调之前表示函数内部没有发生错误。 - azium
1个回答

8
按照惯例,NodeJS使用错误优先的回调函数,这意味着回调函数的第一个参数始终是错误对象。如果没有任何错误,则传递null。换句话说,如果错误参数为null,则操作成功,如果错误参数不为null,则发生了错误。这对你所询问的所有示例都是适用的。如果查看代码,可以看到已经在利用此功能:
User.findOne({ username: username }, function (err, user) {
  if (err) { 
    // Error happened and passed as first argument
    return done(err); 
  } 

  // ...

  // no error so we pass in null
  return done(null, user);

此外,Passport 在其认证流程中有一项惯例,正如他们的文档所说:
如果凭据有效,则 verify 回调函数将调用 done 方法来向 Passport 提供已经通过身份验证的用户。如果凭据无效(例如,密码不正确),则应该调用 done 方法并传递 false 而不是用户,以指示身份验证失败。
这就是你在这里做的事情:
User.findOne({ username: username }, function (err, user) {

  // ...

  if (!user) {
    return done(null, false, { message: 'Incorrect username.' });
  }
  if (!user.validPassword(password)) {
    return done(null, false, { message: 'Incorrect password.' });
  }

  // ...

序列化和反序列化是什么?当会话存储在浏览器中时,登录后会调用序列化函数吗?当访问页面时,反序列化函数将会在服务器上反序列化会话以验证用户是否正确?
好的,Passport文档中写道:
如果身份验证成功,则会通过在用户浏览器中设置的Cookie建立并维护会话。每个后续请求都不包含凭据,而是标识会话的唯一cookie。为了支持登录会话,Passport需要将用户实例序列化到会话中,并从中进行反序列化。
这意味着,在用户登录后,将调用serializeUser, 并传递给回调函数cb中的用户数据。
passport.serializeUser(function(user, cb) {
  cb(null, user); // <-- this user object
});

用户信息保存在会话存储中(通常是浏览器cookie),并在您的代码中通过req.session.passport.user进行访问。

当用户重新连接到您的页面(无论是刷新还是离开后再回来),此数据将作为第一个参数传递给deserializeUser,以用于检索用户对象。

passport.deserializeUser(function(obj, cb) {
  cb(null, obj); // <-- obj is the same `user` object you used in serializeUser
});

你现在所做的是将实际的user对象传递给serializeUser回调函数,然后通过deserializeUser回调函数再次传递相同的对象。这意味着你正在将整个用户对象存储在 cookie 中,这对于玩耍来说没问题,但通常不是一个好主意,因为 cookie 存储是有限的,而且用户信息通常是敏感的。
通常的做法是将用户 id 而不是整个用户对象传递给serializeUser中的cb,以保持会话内存储的数据量较小。当进一步的请求发生时,这个 id 将被传递到deserializeUser中,并用于查找实际的用户对象,通常是从数据库中恢复的,该对象将被还原到req.user中。
以下是一个示例:
passport.serializeUser(function(user, done) {
  done(null, user.id);
});

passport.deserializeUser(function(id, done) {
  User.findById(id, function(err, user) {
    done(err, user);
  });
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接