Node + Express + Passport:req.user未定义

90

我的问题类似于这个问题,但他的解决方案没有深入探讨。

我正在使用Passport来进行Instagram身份验证。成功验证后,用户被重定向到“/”。此时,请求具有用户对象(也就是说它正在工作)。但是,一旦我重定向,req.user就变成了未定义状态。:'(

奇怪的是,每次请求都会调用passport.deserializeUser。它可以成功获取用户对象,但在中间件的某个地方,req.user没有被设置(或被取消设置)。

// on successful auth, goto "/" 
app.get('/', function(req, res) {
    // if the request has the user object, go to the user page
    if (req.user) {
        res.redirect("/user/" + req.user._id);
    }

    res.render("index");
}

app.get('/user/:uid', function(req, res) {
    console.log(req.user) // undefined
}

4
你能发布你的应用程序配置吗?特别是中间件(app.use(...))方面的。可能是因为你的会话cookie过期时间太短,或者你的中间件顺序有误。 - robertklep
中间件的排序很可能是这里的问题。 - Noah
1
我完全按照Passport配置页面上的说明进行中间件设置,但仍然遇到了相同的问题 o.O - vsync
我也有这个问题。不知何故,req.user在90%的情况下未定义。 - Eric Smekens
1
我遇到了同样的问题。 我无法让它工作,而且似乎每次都会重置req.user。 - Gang Su
显示剩余4条评论
30个回答

77

我的问题是在客户端使用fetch时没有指定发送cookie。在请求中包括credentials:'include'字段后,它可以正常工作。

fetch('/api/foo', {credentials: 'include'})

2
稍微解释一下,像 cookies 这样的东西只有在请求来自同一来源时才会默认发送。否则,您必须使用 withCredentials 标志来指定您确实希望发送回 cookies。请参见 https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials。请注意,如果您使用的是 Axios 而不是 fetch API,则语法将不同。 - Adam Zerner

55

你是否为你的应用程序设置了会话状态?如果没有,你需要像这样做...

app.use(session({ secret: 'anything' }));
app.use(passport.initialize());
app.use(passport.session());

30
我按照你写的做了,但我也收到了“undefined”的信息。 - vsync
4
现在express-session已经成为独立的包而不是express的一部分,因此我认为实际上应该是app.use(session({secret : 'anything'}); - gards
2
在设置表达式后,我仍然遇到问题。我在初始化express-session之后调用passport函数,并且可以在应用程序中无问题地使用会话。 - Pietro Coelho
1
@Zlatko 我自己从未尝试过,但你可以尝试这里的答案(https://dev59.com/Sl8e5IYBdhLWcg3wpLwr)。 - Martin
3
这个解决方案对我有效,因为我的应用配置顺序错了。当我按照上面的顺序排列它们时,问题得到了解决。 - fraxture
显示剩余3条评论

19

我对Node非常陌生,但这是一个中间件问题。添加bodyParser并重新排列即可解决。

有问题的代码:

app.use(express.cookieParser('secret'));
app.use(express.cookieSession());
app.use(express.session({ secret: 'anything' }));
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public'))); 

可运行代码:

app.use(express.static(path.join(__dirname, 'public')));
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.session({ secret: 'anything' }));
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);

希望这可以帮到您


2
这对我很有帮助。为什么中间件的第二个顺序起作用而第一个不起作用? - mc9
1
浪费了那么多时间尝试以各种方式调试,最终发现问题出在这里。将 express.static 作为最后一个中间件会破坏与身份验证相关的所有内容。甚至行为都不一致,有时它能工作,有时它不能。我怀疑 express / 上述库中的某些代码非常可疑且容易出现竞争条件。 - Vee6

13

之前我有这段代码(没有起作用):

app.use(express.static(path.join(__dirname, 'public')));
app.use(cookieParser('abcdefg'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(session({
    secret: 'abcdefg',
    resave: true,
    saveUninitialized: false,
    cookie: { secure: true } // this line
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(require('stylus').middleware(path.join(__dirname, 'public')));

然后我从会话初始化器中移除了Cookie选项:

app.use(express.static(path.join(__dirname, 'public')));
app.use(cookieParser('abcdefg'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(session({
    secret: 'abcdefg',
    resave: true,
    saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(require('stylus').middleware(path.join(__dirname, 'public')));

现在它可以正常工作了。


1
@Squirrl 他的Express服务器可能位于代理(Heroku,nginx,IISnode)后面,该代理终止SSL连接。这是正常的。在此处阅读有关此详细解释:https://stackoverflow.com/questions/33871133/secure-cookiesession-when-using-iisnode/34599515#34599515 - Christiaan Westerbeek

7

由于只是从express-session文档中复制粘贴以下代码,而没有完全阅读文档,我遇到了同样的问题。

app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: true,
  cookie: { secure: true }
}))

在说明文档中提到:
然而,它需要启用了https的网站,即必须使用HTTPS来保证cookie安全。如果设置了“secure”,并且您通过HTTP访问您的站点,则不会设置cookie。
因此,如果您只有HTTP连接,请移除“secure”选项,并且以下代码应该正常工作:
app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: true,
  //cookie: { secure: true } remove this line for HTTP connection
}))

谢谢!我为此拼命了两个小时,才意识到几个月前我将 cookie 升级为 HTTPS,并在本地通过 HTTP 进行测试。 - miguelmorin

6

我认为每个人在服务器端都会有一些可能导致这个问题的错误。

要解决此错误,请检查以下内容:

检查1-护照序列化和反序列化

正确的方法(使用Mongoose):

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

passport.deserializeUser((_id, done) => {
  User.findById( _id, (err, user) => {
    if(err){
        done(null, false, {error:err});
    } else {
        done(null, user);
    }
  });
});

检查2 - 会话

检查您的cookie-session包是否已正确配置并运行。检查是否可以在客户端上看到cookie。如果使用express-session,请确保您可以在内存或缓存(Redis)中看到session-id。

检查3 - SSL nginx

确保您的反向代理支持cookie和请求类型。不要忘记检查cookie/会话配置[secure标志]

当我遇到这个错误时,我正在使用(user)作为回调函数。然后我改正了它,改成了(err, user)。现在一切都好了。干杯!


1
谢谢!我为此花费了两个小时的时间挣扎,只意识到几个月前我已将 cookies 升级到 HTTPS,并在本地使用 HTTP 进行测试。 - miguelmorin

4

我在使用express 4时遇到了这个完全相同的问题,在尝试了网上能找到的几乎所有方法后,我最终通过添加'cookie-session'来解决了它。

var cookieSession = require('cookie-session');

app.use(cookieSession({
  keys: ['key1', 'key2']
}));

4

像@Martin所说的那样启用会话。但是,如果您正在使用Express 4.*版本,则不会捆绑诸如会话之类的中间件,因此您需要单独安装它。

  1. 在您的package.json中添加"express-session": "1.4.0"
  2. npm install
  3. 使用它

;

var express = require('express');
var cookieParser = require('cookie-parser')
var session = require('express-session')
var app = express()
app.use(cookieParser()) // required before session.
app.use(session({secret: 'keyboard cat'}))

欲了解更多信息,请查看 Express Session文档


根据您提供的文档,不再建议使用Cookie解析器。 - counterbeing

3
当您对用户进行身份验证时,请求值req.user将被填充。为了知道用户的身份并填充该值,会使用cookie。 如果像我一样配置了带有安全cookie的会话,则cookie仅在HTTPS上可用,这不是例如本地开发服务器的标准配置。要使cookie在HTTP上工作,请注释掉cookie: { secure: true },并且req.user值将被正确配置:
this.app.use(session({
    resave: true,
    saveUninitialized: true,
    secret: process.env.SESSION_SECRET || "a secret",
    // cookie: { secure: true },
}));

如果你希望在生产环境中仅使用 HTTPS 来处理cookie,可以采用以下方式:

cookie: { secure: process.env.ENV === 'PRODUCTION' }

2
非常感谢!我已经遭受了一段时间的这个问题。 - Mathyou
谢谢!我为此苦恼了两个小时,才意识到几个月前我已经将 cookie 升级到 HTTPS,并在本地使用 HTTP 进行测试。 - miguelmorin

2

'req.user'在经过passport.auth中间件后才会为我设置用户。我来到这个线程是想找出如何从其他请求中访问用户名(不使用passport.auth中间件)。如果有人遇到了这个问题,请尝试以下方法:

req._passport.session.user;

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