护照.js - 错误:无法将用户序列化为会话

218

我在使用Passport.js模块和Express.js时遇到了问题。

这是我的代码,我只想先尝试使用硬编码的登录。

我总是收到以下消息:

我搜索了很多,在stackoverflow上找到了一些帖子,但我没有找到失败的原因。

Error: failed to serialize user into session
    at pass (c:\Development\private\aortmann\bootstrap_blog\node_modules\passport\lib\passport\index.js:275:19)

我的代码看起来像这样。

'use strict';

var express = require('express');
var path = require('path');
var fs = require('fs');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var nodemailer = require('nodemailer');

var app = express();

module.exports = function setupBlog(mailTransport, database){
var config = JSON.parse(fs.readFileSync('./blog.config'));

app.set('view options', {layout: false});

app.use(express.static(path.join(__dirname, '../', 'resources', 'html')));


app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({ secret: 'secret' }));
app.use(passport.initialize());
app.use(passport.session());


app.get('/blog/:blogTitle', function(req, res) {
  var blogTitle = req.params.blogTitle;
  if(blogTitle === 'newest'){
    database.getLatestBlogPost(function(post) {
      res.send(post);
    });
  } else {
    database.getBlogPostByTitle(blogTitle, function(blogPost) {
      res.send(blogPost);
    });
  }
});

passport.use(new LocalStrategy(function(username, password, done) {
  // database.login(username, password, done);
  if (username === 'admin' && password === 'admin') {
    console.log('in');
    done(null, { username: username });
  } else {
    done(null, false);
  }
}));

app.post('/login', passport.authenticate('local', {
  successRedirect: '/accessed',
  failureRedirect: '/access'
}));





app.listen(8080);
console.log('Blog is running on port 8080');

}();

谢谢。

11个回答

434

看起来你没有实现passport.serializeUserpassport.deserializeUser。尝试添加以下内容:

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

passport.deserializeUser(function(user, done) {
  done(null, user);
});

2
对我来说可行。为什么我不必像其他地方写的那样使用“id”这个部分? - schlenger
11
这取决于您如何实现序列化。有时您按用户ID进行序列化,这意味着 serializeUser 函数仅在会话中存储用户ID,而 deserializeUser 函数使用该ID从数据库中检索用户数据(例如)。这是为了防止会话存储包含实际的用户数据本身。 - robertklep
+1 @robertklep 的评论。我个人总是避免序列化用户信息,只使用 id(出于膨胀/性能原因)。 - electblake
2
@robertklep 我不想将它存储在会话中。我正在使用JWT。是否需要serializeUser/deserializeUser?我想要一个无会话。我正在使用JWT。 - Internial
@robertklep 我需要它们。你能否提供一个示例或教程/书籍参考来帮助我?这是我的当前代码。http://textuploader.com/dlkxz - Internial
显示剩余5条评论

61

如果您决定不使用会话,您可以将会话设置为false。

app.post('/login', passport.authenticate('local', {
  successRedirect: '/accessed',
  failureRedirect: '/access',
  session: false
}));

1
但这不会关闭所有其他端点上的会话(我也不希望有任何会话)。 - jayarjo

20

听起来你错过了 passportjs 配置的一部分,具体来说是这两个方法:

passport.serializeUser(function(user, done) {
    done(null, user._id);
    // if you use Model.id as your idAttribute maybe you'd want
    // done(null, user.id);
});

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

我加了一点关于._id.id的内容,但这段代码片段来自文档的配置部分。再读一遍并祝你好运 :)


4
这里有一种有效但还是有些懒惰的方法来使用会话并仍然“序列化”值。
var user_cache = {};

passport.serializeUser(function(user, next) {
  let id = user._id;
  user_cache[id] = user;
  next(null, id);
});

passport.deserializeUser(function(id, next) {
  next(null, user_cache[id]);
});

如果出现奇怪的错误,请问自己:“我真的在用户对象中设置了'_id'吗?” - 在大多数情况下,您并没有。因此,请使用适当的属性作为键。


2

确保在获取用户数据时使用了async和await。

passport.serializeUser((user, done) => {
   done(null, user.id);
});

passport.deserializeUser(async (id, done) => {
  const USER = await User.findById(id);
  done(null, USER);
});

passport.use(
  new GoogleStrategy(
    {
      // options for google strategy
      clientID: keys.google.clientID,
      clientSecret: keys.google.clientSecret,
      callbackURL: "/auth/google/redirect",
    },
    async (accessToken, refreshToken, profile, done) => {
      //   passport callback function

      //   check if user already exist in our db
      const oldUser = await User.findOne({ googleId: profile.id });
      if (oldUser) {
        return done(null, oldUser);
      } else {
        const newUser = await new User({
          username: profile.displayName,
          googleId: profile.id,
        }).save();
        return done(null, newUser);
      }
    }
  )
);

1

将此代码用于您的server.js文件中,您可以在其中发出get或post请求。

  app.get('/auth/facebook/callback',passport.authenticate('facebook',{
    successRedirect:'/profile',
    failureRdirect:'/',
    session: false
}))

0

使用Promise与serializeUser和deserializeUser:

passport.serializeUser((user, done) => {
  done(null, user.id);
});

passport.deserializeUser((id, done) => {
  // console.log(`id: ${id}`);
  User.findById(id)
    .then((user) => {
      done(null, user);
    })
    .catch((error) => {
      console.log(`Error: ${error}`);
    });
});

请查看我的GitHub存储库,以获取完整的代码示例,了解如何解决此问题。

0

由于“user”作为对象数组返回,请尝试以下操作:

passport.serializeUser(function(user, cb) {
    process.nextTick(function() {
        return cb(null, user[0].id);
    });
});

0
今天我发现的另一个普遍原因是,如果你正在使用标准包(如passport、passport-local、passport-local-mongoose和express-session),并且你已经处于一个会话中,也就是说你已经登录了,然后终止服务器并重新启动,那么在登录时你将仍然处于同一个会话中,浏览器会一直加载而没有响应,并且会出现"failed to serialize user into the session"的错误。

0

你漏了这段代码:

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

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

详情请查看http://www.passportjs.org/docs/configure/的会话部分。


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