护照的req.isAuthenticated始终返回false,即使我硬编码done(null, true)。

58

我正在尝试让我的Passport本地策略起作用。

我已经设置了这个中间件:

passport.use(new LocalStrategy(function(username, password, done) {
    //return done(null, user);
    if (username=='ben' && password=='benny'){
        console.log("Password correct");
        return done(null, true);
    }
    else
        return done(null, false, {message: "Incorrect Login"});
}));

但是在这里

app.use('/admin', adminIsLoggedIn, admin);

function adminIsLoggedIn(req, res, next) {

    // if user is authenticated in the session, carry on 
    if (req.isAuthenticated())
        return next();

    // if they aren't redirect them to the home page
    res.redirect('/');
}

这个网站总是失败并重定向到主页。

我想不出为什么会发生这种情况?为什么它无法进行身份验证?

在我的控制台中,我可以看到“密码正确”正在打印。为什么它不能工作?

21个回答

71

我曾经遇到类似的问题。可能是因为需要使用passport的express-session中间件。通过按照以下顺序使用中间件进行修复:(Express 4)

var session = require('express-session');

// required for passport session
app.use(session({
  secret: 'secrettexthere',
  saveUninitialized: true,
  resave: true,
  // using store session on MongoDB using express-session + connect
  store: new MongoStore({
    url: config.urlMongo,
    collection: 'sessions'
  })
}));

// Init passport authentication 
app.use(passport.initialize());
// persistent login sessions 
app.use(passport.session());

我也遇到了同样的问题...但是还没有找到解决方案。 - Nodemon
5
我已经按照你上面说的做了,但仍然面临这个问题。 - Nodemon
12
我的问题是,我调用了 'passport.use,serializeUser,deserializeUser',但在此之前我没有调用 'app.use(passport.initialize());' 和 'app.use(passport.session());'。 - osmingo
@user2988146:很好的发现,我也犯了同样的错误,但现在它可以工作了! - Ben
1
我也遇到了同样的问题,@Nitin你救了我的一天!为什么顺序如此重要以至于会毁掉一切呢? - chiahao
显示剩余3条评论

18

新手必读

我曾经遇到过类似的问题,我的isAuthenticated()函数总是返回false。我浪费了很多时间,希望这个答案能帮助你。

一些需要注意的常见问题:

  1. 中间件设置顺序(express-session > pass.initialize > pass.session)。
  2. 序列化和反序列化方法需要在请求上传递用户信息。(更多信息请参考我在此链接上发布的答案:Passport Session基础知识(expressjs)-为什么需要序列化和反序列化?),如果请求上没有用户信息,则isAuthenticated会返回false...并重定向到定义的路径......当返回false时...
  3. 在模型(user.js)中定义的getUserById或findById函数需要定义一个User.findById(而不是User.findOne)函数。(这个函数将在每个会话中加载用户信息到请求上)。

谢谢伙计,我找了很久但是没有找到任何解决方案,最终你给了我一个解决方案。 - Syed Muhammad Asad
我之前在findById()中使用了findOne(),现在将其替换为了find(),程序正常运行。为什么findOne()会出问题? - Syed Muhammad Asad
2
我实际上需要使用findOne而不是findById,因为我正在尝试对用户的电子邮件进行不区分大小写的查找。根据文档,findById只是findOne的语法糖。我认为要求使用findById不应该成为被接受解决方案的一部分。 - sarora

15

这也可能是由于您的客户端POST/GET调用引起的问题。我曾经遇到过完全相同的问题,但后来发现我必须给fetch(这是我使用的)提供credentials:'include'选项,如下所示:

fetch('/...', {
  method: 'POST',
  headers: myHeaders,
  credentials: 'include',
  body: ...
  ...})

原因是因为fetch不支持传递cookie,这在此情况下是必要的。


谢谢Alex!你帮我省了很多时间。 我遇到了这个问题,但是发现结果在不同的浏览器中是不同的。 所以我确定这是fetch兼容性问题! - Huiyang Shan
谢谢Alex,如果使用axios,可能需要添加{ withCredentials: true }。 - Paschal
谢谢!!!如果使用Express,您还必须在CORS中间件中包含origin并将credentials设置为true,否则您将收到错误。 - Giovanni El Zelah

14
我的问题是,即使数据不通过https传输,我还是将cookie.secure设置为true了。
app.use(require('express-session')({
    secret: process.env.sessionSecret,
    cookie: {
        maxAge: 1000 * 60 * 60 * 24 * 7 // 1 week
    },
    store: store,
    resave: false,
    saveUninitialized: false,
    cookie: { secure: false } // Remember to set this
}));

如果您没有使用https,请记得将cookie设置为false。

cookie: { secure: false } // Set to false

另外,如果您确信自己已经启用了 HTTPS,请务必信任代理。

app.set('trust proxy', 1) // trust first proxy

1
这对我有效。 补充一点:不需要设置 cookie: { secure: false },因为它是默认值。 - fjplaurr

8
我曾经遇到过同样的问题,那是因为我忘记添加

标签。
request.login()

on

app.post('/login', 
    function(request, response, next) {
        console.log(request.session)
        passport.authenticate('login', 
        function(err, user, info) {
            if(!user){ response.send(info.message);}
            else{

                request.login(user, function(error) {
                    if (error) return next(error);
                    console.log("Request Login supossedly successful.");
                    return response.send('Login successful');
                });
                //response.send('Login successful');
            }

        })(request, response, next);
    }
);

希望这能帮助到其他像我一样遇到同样问题的人。

在 passport.authenticate 中,用户信息为 false 并提示凭据丢失。你能知道错误在哪吗?请参考我的问题说明:http://stackoverflow.com/questions/42898027/passport-js-local-strategy-custom-callback-showing-user-as-false-and-info-as-mi - charan tej
@OE1,谢谢。你救了我。 - Vikram
2
注意:passport.authenticate()中间件会自动调用req.login()。当用户注册时,可以调用req.login()函数自动登录新注册的用户。 - Epirocks
非常感谢,太棒了。基本上,每当我们使用 Passport 手动传递回调进行身份验证时,我们必须明确登录用户,Passport 不会自动为我们执行。 - Ankur Thakur

3
有一个问题在passport.js中很少被提及,但我发现了。这就是为什么你可以创建一个帐户或登录并且一开始认证成功,但后来你会发现整个应用程序的req.user未定义或req.isAuthenticated()false认证后,passport.js需要重新路由/重定向。这是passport初始化实际会话的方式。
  signIn(req, res, next) {
    passport.authenticate("local")(req, res, function() {
      if (!req.user) {
        console.log("User not found!");
      } else {
        res.redirect("/")
        console.log("signed in")
      }
    })
  }


如果在认证之后没有重定向,它甚至不会启动您的会话作为 "req.user"和"req.isAuthenticated()"将是假的。我的应用程序是一个React和Node应用程序,但对于Node应用程序和React / Node应用程序都是正确的。"最初的回答".

这是一个有趣的答案。这个要求在哪里有记录吗?我觉得这不合理!我实际上已经在我的代码中尝试了res.redirect("/"),但它在浏览器中失败了(它不喜欢这个响应)。 - Andy Lorenz
你能提供一下你发现需要重定向的信息来源吗?我感觉护照文件不是最好的选择 :( - benwl

2

我也遇到了同样的问题,在网上找不到任何解决方案,但我自己想出来了。

app.use(require("express-session")({
secret: "This is the secret line",
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(bodyParser.urlencoded({extended: true}));

express-session 的要求和使用应该在任何其他使用之前。尝试这个,我确定这会起作用,对我有用!


护照(Passport)的req.isAuthenticated()总是返回false,即使我硬编码了done。 - Siful Islam

1
对于我来说,我按照所有答案所说的做了,但是它无法登录,至少大部分时间都是这样。然而,奇怪的是,如果我在Chrome开发者工具中将我的网络限制为3G,登录就可以正常工作。我的问题是会话没有保存。我可以在登录后拥有用户,但是一旦下一个请求进来,它就会失去。我的解决方案是使用req.session.save回调函数(如此处所建议的)和req.logIn()回调函数:
router.post("/login", (req, res, next) => {
    passport.authenticate("local", {
        failureFlash: true,
        //failureRedirect: /login", // these don't work!
        //successRedirect: "/home", // because they redict too fast
    }, (err, user, info) => {
        if (err !== null || user === false) {
            req.session.save(() => {
                // failureRedirectin the callback here
                res.redirect("/login");
            });
        } else {
            req.logIn(user, err => {
                // save session before redirecting!
                req.session.save(() => {
                    // successRedirectin the callback here
                    res.redirect("/home");
                });
            });
        }
    })(req, res, next);
});

在重定向之前,等待护照会话保存后才允许下一个请求通过,从而解决了竞态条件问题。

不确定这是否仅适用于本地策略。使用express-session。


1

我遇到了同样的问题,问题是由于我使用的浏览器默认阻止了Cookies。为了解决这个问题,我将我的应用程序URL添加到允许使用Cookies的网站列表中。


1
我知道现在已经很晚了,但是我遇到了一个与Facebook登录策略相关的问题。它一直很正常,突然间就在Safari中无法使用了。我试过了所有上述的解决方案,但没有一个有效。最终,在Chrome网页控制台中发现了一个线索,仅在Chrome中仍然可以使用。警告如下: 与跨站资源相关联的cookie在未设置SameSite属性的情况下被设置。Chrome的未来版本将仅在设置了SameSite=NoneSecure的情况下才能传递跨站请求的cookie。 只有在那时我才意识到,在express session中不应该设置Samesite:true,因为这样会导致Facebook登录时的cookie无法设置。经过数天的尝试,我通过将samesite更改为"none"来解决了这个问题。
希望这可以帮助未来遇到此问题的人们。

它帮了我!谢谢。我用这个修复了我的https网站没有跨站点请求身份验证的问题。我的代码在这里:https://github.com/vcvcchaturvedi/job-helper-be/blob/master/index.js - vinaych
在客户端调用请求时,还要使用 withCredentials: true! - vinaych

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