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

IT技术 javascript node.js express logout passport.js
2021-02-20 01:25:33

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

非常感谢!

/******* 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();
}
6个回答

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

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

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

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

希望这可以帮助!

好的,我用你的第二种方法。会话文件夹中的会话文件被删除,会话 cookie 被从客户端删除,但节点服务器在控制台中显示以下错误消息:[session-file-store] will retry, error on last attempt: Error: ENOENT: no such file or directory, open ...我该如何解决这个问题?
2021-04-22 01:25:33
你是根据这个client-sessions吗?它的销毁函数不带参数:github.com/mozilla/node-client-sessions/blob/master/lib/...
2021-04-26 01:25:33
请注意,在此处给出的示例中, session.destroy 的使用假定使用 express-sessions module。其他会话module(例如 mozilla node-client-sessions)添加了一个仅同步返回并忽略任何回调参数的 destroy 方法。在这种情况下,您的代码将挂起
2021-05-09 01:25:33
@schlenger 嗯,以上使用的是 Express 3。您使用的是版本 4 吗?
2021-05-13 01:25:33
我只是得到 'Object #<Session> has no method 'destroy'' 作为错误消息
2021-05-15 01:25:33

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

我不喜欢不使用req.logout(),的想法,为了向前兼容等等,所以我只是req.session.destroy();在后面添加req.logout(),效果很好。
2021-04-23 01:25:33
req.session.destroy(); 也为我工作,记得用 res.clearCookie('cookiename') 清除你的 cookie。但是有谁知道 req.logout() 到底是做什么的?
2021-05-05 01:25:33
@WebHrushi 您可以在passport/lib/passport/http/request.js 中找到该函数。今天这两个版本都停止为我工作,重定向似乎在会话最终被销毁之前以两种方式调用。在重定向页面上,这里描述的两种方式似乎都运行良好。
2021-05-07 01:25:33
添加 req.session.destroy(); 为我工作,我遇到了同样的问题
2021-05-16 01:25:33

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

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

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

这就是可能发生的事情:

  1. 收到请求 1(任何请求)
  2. Req 1 将会话从 redis 加载到内存
  3. 收到注销请求
  4. 注销请求加载会话
  5. 注销请求会破坏会话
  6. 注销请求发送重定向到浏览器(cookie 不会被删除)
  7. Req 1 完成处理
  8. Req 1 将会话从内存保存到redis
  9. 用户在没有登录对话框的情况下打开页面,因为 cookie 和会话都已就位

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

这解决了客户端上的问题,而不是服务器上的问题,这似乎不安全。如果connect.sid在您注销之前被劫持,则不应在您注销后允许其他方以您的身份登录。
2021-05-05 01:25:33

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

(坏的)

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

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

(好的)

fetch('/auth/logout', { credentials: 'same-origin' })
  .then([other stuff]);
在得到这个答案之前搜索了 4 个小时,这是一个愚蠢的错误,但感谢您指出。
2021-05-11 01:25:33

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

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

编辑:这不再是问题。

当前版本的 Passport 接受注销或注销(与登录和登录相同)
2021-04-18 01:25:33
logoutlogOut在 2015 年互为别名:github.com/jaredhanson/passport/blame/...
2021-04-27 01:25:33