Javascript 为什么 Node 中的 PassportJS 不会在注销时删除会话

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13758207/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 12:49:44  来源:igfitidea点击:

Why is PassportJS in Node not removing session on logout

javascriptnode.jsexpresslogoutpassport.js

提问by Jeffrey Chen

I am having trouble getting my system to log out with PassportJS. It seems the logout route is being called, but its not removing the session. I want it to return 401, if the user is not logged in in specific route. I call authenticateUser to check if user is logged in.

我在使用 PassportJS 注销系统时遇到问题。似乎正在调用注销路由,但它没有删除会话。如果用户未登录特定路线,我希望它返回 401。我调用 authenticationUser 来检查用户是否登录。

Thanks a lot!

非常感谢!

/******* This in index.js *********/
// setup passport for username & passport authentication
adminToolsSetup.setup(passport);

// admin tool login/logout logic
app.post("/adminTool/login",
    passport.authenticate('local', {
        successRedirect: '/adminTool/index.html',
        failureRedirect: '/',
        failureFlash: false })
);
app.get('/adminTool/logout', adminToolsSetup.authenticateUser, function(req, res){
    console.log("logging out");
    console.log(res.user);
    req.logout();
    res.redirect('/');
});


// ******* This is in adminToolSetup ********
// Setting up user authentication to be using user name and passport as authentication method,
// this function will fetch the user information from the user name, and compare the password     for authentication
exports.setup = function(passport) {
    setupLocalStrategy(passport);
    setupSerialization(passport);
}

function setupLocalStrategy(passport) {
    passport.use(new LocalStrategy(
        function(username, password, done) {
            console.log('validating user login');
            dao.retrieveAdminbyName(username, function(err, user) {
                if (err) { return done(err); }
                if (!user) {
                    return done(null, false, { message: 'Incorrect username.' });
                }
                // has password then compare password
                var hashedPassword = crypto.createHash('md5').update(password).digest("hex");
                if (user.adminPassword != hashedPassword) {
                    console.log('incorrect password');
                    return done(null, false, { message: 'Incorrect password.' });
                }
                console.log('user validated');
                return done(null, user);
            });
        }
    ));
}

function setupSerialization(passport) {
    // serialization
    passport.serializeUser(function(user, done) {
        console.log("serialize user");
        done(null, user.adminId);
    });

    // de-serialization
    passport.deserializeUser(function(id, done) {
        dao.retrieveUserById(id, function(err, user) {
            console.log("de-serialize user");
            done(err, user);
        });
    });
}

// authenticating the user as needed
exports.authenticateUser = function(req, res, next) {
    console.log(req.user);
    if (!req.user) {
        return res.send("401 unauthorized", 401);
    }
    next();
}

回答by jlmakes

Brice's answer is great, but I still noticed an important distinction to make; the Passport guide suggests using .logout()(also aliased as .logOut()) as such:

Brice 的回答很好,但我仍然注意到一个重要的区别;护照指南建议使用.logout()(也别名为.logOut()):

app.get('/logout', function(req, res){
  req.logout();
  res.redirect('/'); //Can fire before session is destroyed?
});

But as mentioned above, this is unreliable. I found it behaved as expected when implementing Brice's suggestion like this:

但如上所述,这是不可靠的。我发现在执行 Brice 的建议时,它的行为符合预期:

app.get('/logout', function (req, res){
  req.session.destroy(function (err) {
    res.redirect('/'); //Inside a callback… bulletproof!
  });
});

Hope this helps!

希望这可以帮助!

回答by Brice

Ran into the same issue. Using req.session.destroy();instead of req.logout();works, but I don't know if this is the best practice.

遇到了同样的问题。使用req.session.destroy();代替req.logout();有效,但我不知道这是否是最佳实践。

回答by esp

session.destroymay be insufficient, to make sure the user is fully logged out you have to clear session cookie as well.

session.destroy可能还不够,为了确保用户完全退出,您还必须清除会话 cookie。

The issue here is that if your application is also used as an API for a single page app (not recommended but quite common) then there can be some request(s) being processed by express that started before logout and end after logout. If this were the case then this longer running request will restore the session in redis after it was deleted. And because the browser still has the same cookie the next time you open the page you will be successfully logged in.

这里的问题是,如果您的应用程序也用作单页应用程序的 API(不推荐但很常见),那么 express 可能会处理一些在注销前开始并在注销后结束的请求。如果是这种情况,那么这个运行时间较长的请求将在删除后恢复 redis 中的会话。并且因为浏览器在您下次打开页面时仍然具有相同的 cookie,您将成功登录。

req.session.destroy(function() {
    res.clearCookie('connect.sid');
    res.redirect('/');
});

That's the what maybe happening otherwise:

这就是可能发生的事情:

  1. Req 1 (any request) is received
  2. Req 1 loads session from redis to memory
  3. Logout req received
  4. Logout req loads session
  5. Logout req destroys session
  6. Logout req sends redirect to the browser (cookie is not removed)
  7. Req 1 completes processing
  8. Req 1 saves the session from memory to redis
  9. User opens the page without login dialog because both the cookie and the session are in place
  1. 收到请求 1(任何请求)
  2. Req 1 将会话从 redis 加载到内存
  3. 收到注销请求
  4. 注销请求加载会话
  5. 注销请求会破坏会话
  6. 注销请求发送重定向到浏览器(cookie 不会被删除)
  7. Req 1 完成处理
  8. Req 1 将会话从内存保存到redis
  9. 用户在没有登录对话框的情况下打开页面,因为 cookie 和会话都已就位

Ideally you need to use token authentication for api calls and only use sessions in web app that only loads pages, but even if your web app is only used to obtain api tokens this race condition is still possible.

理想情况下,您需要对 api 调用使用令牌身份验证,并且仅在仅加载页面的 Web 应用程序中使用会话,但即使您的 Web 应用程序仅用于获取 api 令牌,这种竞争条件仍然可能存在。

回答by nrflaw

I was having the same issue, and it turned out to not be a problem with Passport functions at all, but rather in the way I was calling my /logoutroute. I used fetch to call the route:

我遇到了同样的问题,结果证明根本不是 Passport 功能的问题,而是我调用/logout路线的方式。我使用 fetch 来调用路由:

(Bad)

(坏的)

fetch('/auth/logout')
  .then([other stuff]);

Turns out doing that doesn't send cookies so the session isn't continued and I guess the res.logout()gets applied to a different session? At any rate, doing the following fixes it right up:

事实证明这样做不会发送 cookie,因此会话不会继续,我猜这res.logout()会应用于不同的会话?无论如何,执行以下操作即可解决问题:

(Good)

(好的)

fetch('/auth/logout', { credentials: 'same-origin' })
  .then([other stuff]);

回答by abitofcode

I was having the same issues, capital O fixed it;

我遇到了同样的问题,资本 O 修复了它;

app.get('/logout', function (req, res){
  req.logOut()  // <-- not req.logout();
  res.redirect('/')
});

Edit: this is no longer an issue.

编辑:这不再是问题。

回答by Charlie Fish

I was recently having this same issue and none of the answers fixed the issue for me. Could be wrong but it does seem to have to do with a race condition.

我最近遇到了同样的问题,但没有一个答案为我解决了这个问题。可能是错误的,但它似乎与竞争条件有关。

Changing the session details to the options below seems to have fixed the issue for me. I have tested it about 10 times or so now and everything seems to be working correctly.

将会话详细信息更改为下面的选项似乎已经为我解决了这个问题。我现在已经测试了大约 10 次,一切似乎都正常工作。

app.use(session({
    secret: 'secret',
    saveUninitialized: false,
    resave: false
}));

Basically I just changed saveUninitializedand resavefrom trueto false. That seems to have fixed the issue.

基本上我只是改变saveUninitializedresavetruefalse。这似乎解决了这个问题。

Just for reference I'm using the standard req.logout();method in my logout path. I'm not using the session destroy like other people have mentioned.

仅供参考,我req.logout();在注销路径中使用标准方法。我没有像其他人提到的那样使用会话销毁。

app.get('/logout', function(req, res) {
    req.logout();
    res.redirect('/');
});

回答by sstauross

I used both req.logout()and req.session.destroy()and works fine.

我使用了req.logout()andreq.session.destroy()并且工作正常。

server.get('/logout', (req, res) => {
  req.logout();
  req.session.destroy();
  res.redirect('/');
});

Just to mention, i use Redis as session store.

顺便提一下,我使用 Redis 作为会话存储。

回答by Jongz Puangput

I got an experience that, sometime it's doesn't work because you fail to to setup passport properly. For example, I do vhost, but on main app I setup passport like this which is wrong.

我的经验是,有时它不起作用,因为您未能正确设置护照。例如,我这样做vhost,但在主应用程序上我设置了这样的护照,这是错误的。

app.js(why wrong ? please see blockqoute below)

app.js(为什么错?请看下面的blockqoute)

require('./modules/middleware.bodyparser')(app);
require('./modules/middleware.passport')(app);
require('./modules/middleware.session')(app);
require('./modules/app.config.default.js')(app, express);

// default router across domain
app.use('/login', require('./controllers/loginController'));
app.get('/logout', function (req, res) {
    req.logout();
    res.redirect('/');
});

// vhost setup
app.use(vhost('sub1.somehost.dev', require('./app.host.sub1.js')));
app.use(vhost('somehost.dev', require('./app.host.main.js')));

actually, it must not be able to login, but I manage to do that because, I continue to do more mistake. by putting another passport setup here, so session form app.jsavailable to app.host.sub1.js

实际上,它一定无法登录,但我设法做到了,因为我继续犯更多错误。通过在此处放置另一个护照设置,因此会话表app.js可用于app.host.sub1.js

app.host.sub1.js

app.host.sub1.js

// default app configuration
require('./modules/middleware.passport')(app);
require('./modules/app.config.default.js')(app, express);

So, when I want to logout... it's not work because app.jswas do something wrong by start initialize passport.jsbefore express-session.js, which is wrong !!.

所以,当我想注销时......这是行不通的,因为app.js在 start initialize passport.jsbefore 之前做错了什么express-session.js,这是错误的!!

However, this code can solved the issues anyway as others mention.

但是,正如其他人提到的那样,此代码无论如何都可以解决这些问题。

app.js

应用程序.js

app.get('/logout', function (req, res) {
    req.logout();
    req.session.destroy(function (err) {
        if (err) {
            return next(err);
        }

        // destroy session data
        req.session = null;

        // redirect to homepage
        res.redirect('/');
    });
});

But in my case the correct way is... swap the express-session.jsbefore passport.js

但在我的情况下,正确的方法是......在passport.js之前交换express-session.js

documentalso mention

文件还提到

Note that enabling session support is entirely optional, though it is recommended for most applications. If enabled, be sure to use express.session() before passport.session() to ensure that the login session is restored in the correct order.

请注意,启用会话支持完全是可选的,但建议大多数应用程序使用它。如果启用,请确保在passport.session() 之前使用express.session() 以确保以正确的顺序恢复登录会话。

So, resolved logout issue on my case by..

因此,解决了我的情况下的注销问题...

app.js

应用程序.js

require('./modules/middleware.bodyparser')(app);
require('./modules/middleware.session')(app);
require('./modules/middleware.passport')(app);
require('./modules/app.config.default.js')(app, express);


// default router across domain
app.use('/login', require('./controllers/loginController'));
app.get('/logout', function (req, res) {
    req.logout();
    res.redirect('/');
});

app.host.sub1.js

app.host.sub1.js

// default app configuration
require('./modules/app.config.default.js')(app, express);

and now req.logout();is work now.

现在req.logout();是工作。

回答by DontRelaX

Destroying session by yourself looks weird. I faced with this issue having next configuration:

自己销毁会话看起来很奇怪。我在下一个配置时遇到了这个问题:

"express": "^4.12.3",
"passport": "^0.2.1",
"passport-local": "^1.0.0",

I should say that this configuration works well. The reason of my issue was in custom sessionStorethat I defined here:

我应该说这个配置运行良好。我的问题的原因是我在sessionStore这里定义的自定义:

app.use(expressSession({
    ...
    store: dbSessionStore,
    ...
}));

To be sure that your issue here too just comment store line and run without session persisting. If it will work you should dig into your custom session store. In my case setmethod was defined wrong. When you use req.logout()session store destroy()method not invoked as I thought before. Instead invoked setmethod with updated session.

确保您的问题在这里也只是评论存储行并在没有会话持续的情况下运行。如果它可以工作,您应该深入了解您的自定义会话存储。在我的情况下,set方法定义错误。当您使用req.logout()会话存储destroy()方法时,没有像我之前想的那样调用。而是set使用更新的会话调用方法。

Good luck, I hope this answer will help you.

祝你好运,我希望这个答案对你有帮助。

回答by user1733031

None of the answers worked for me so I will share mine

没有一个答案对我有用,所以我会分享我的

app.use(session({
    secret: 'some_secret',
    resave: false,
    saveUninitialized: false,
   cookie: {maxAge: 1000} // this is the key
}))

and

router.get('/logout', (req, res, next) => {
    req.logOut()
    req.redirect('/')
})