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

问题描述 投票:63回答:20

我无法让我的系统注销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();
}
javascript node.js express logout passport.js
20个回答
74
投票

布莱斯的答案很棒,但我仍然注意到了一个重要的区别; 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!
  });
});

希望这可以帮助!


1
投票

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

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

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

0
投票

我遇到了同样的问题。原来我的护照版本与Express 4.0不兼容。只需要安装旧版本。

    npm install --save [email protected]

0
投票

这对我有用:

app.get('/user', restrictRoute, function (req, res) {
  res.header('Cache-Control', 'no-cache, private, no-store, must-revalidate,
              max-stale=0, post-check=0, pre-check=0');
});

它确保您的页面不会存储在缓存中


0
投票

我正在与程序员合作,建议删除req的用户:

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

原因:我们需要从req中删除(passportjs也这样做但是异步方式),因为注销后没有使用用户数据,即使这样可以节省内存,也可能是passportjs找到的用户数据,可能会创建新的会话和重定向(但不是通过这种方式,这是我们的责任去除无关紧要的事情。 PassportJS在登录后将数据分配到req.user并且如果我们使用req.logout()也会删除但是它可能无法正常工作,因为NodeJS异步性质


0
投票

我遇到了与Passport 0.3.2类似的问题。

当我使用自定义回拨进行护照登录和注册时,问题仍然存在。

通过升级到Passport 0.4.0并添加行来解决问题

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

0
投票

显然,这个问题有多种可能的原因。在我的情况下,问题是声明的顺序错误,即在护照初始化之前声明了注销终点。正确的顺序是:

app.use(passport.initialize());
app.use(passport.session());


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

0
投票

由于您使用的是通过connect.sid cookie使用自己的会话的护照身份验证,这种处理登出的最简单方法是让护照处理会话。

app.get('/logout', function(req, res){
  if (req.isAuthenticated()) {
    req.logOut()
    return res.redirect('/') // Handle valid logout
  }

  return res.status(401) // Handle unauthenticated response
})

0
投票

这里的所有示例都在req.session.destroy之后进行重定向。但是要意识到Express会立即为您重定向到的页面创建一个新会话。与Postman结合使用后,我发现了一个奇怪的行为,即在注销后立即执行Passport-Login会产生Passport成功但无法将用户ID存储到会话文件的效果。原因是邮递员需要更新该组的所有请求中的cookie,这需要一段时间。此外,破坏回调中的重定向也无济于事。

我通过不进行重定向但只返回一条json消息解决了这个问题。


-1
投票

您应该使用req.logout()来销毁浏览器中的会话。

app.get('/logout', function(req, res) {
    req.logout();
    res.redirect('/'); // whatever the route to your default page is
});

-3
投票

我不知道如何,但ng-href="/signout"解决了我的问题。以前我已经使用服务注销,但我直接使用它。


41
投票

陷入同样的​​问题。使用req.session.destroy();而不是req.logout();作品,但我不知道这是否是最好的做法。


-3
投票

在我的情况下,使用传递给req.session.destroy的回调只帮助了一些时间,我不得不诉诸这个黑客:

req.session.destroy();
setTimeout(function() {
    res.redirect "/";
}, 2000);

我不知道为什么这是我能够开始工作的唯一解决方案,但不幸的是@ JulianLloyd的答案对我来说并不一致。

这可能与我的实时登录页面使用SSL(我无法在登台站点或本地主机上重现该问题)这一事实有关。我的应用程序中可能还有其他内容;我正在使用derby-passport模块,因为我的应用程序使用的是Derby框架,所以很难找出问题所在。

这显然是一个时间问题,因为我首先尝试了100毫秒的超时,这还不够。

不幸的是,我还没有找到更好的解决方案。


10
投票

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

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

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

这就是可能发生的事情:

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

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


7
投票

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

(坏)

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

结果这样做不发送cookie所以会话不会继续,我想res.logout()应用于不同的会话?无论如何,执行以下操作会修复它:

(好)

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

5
投票

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

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

编辑:这不再是一个问题。


4
投票

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

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

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

基本上我只是将saveUninitializedresavetrue改为false。这似乎解决了这个问题。

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

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

4
投票

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

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

简而言之,我使用Redis作为会话存储。


1
投票

自己摧毁会议看起来很奇怪。我遇到了下一个配置的问题:

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

我应该说这种配置效果很好。我的问题的原因是我在这里定义的自定义sessionStore

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

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

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


1
投票

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

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')));

实际上,它一定不能登录,但我设法做到这一点因为,我继续做更多的错误。通过在这里设置另一个护照设置,所以app.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);

因此,当我想退出时...它不起作用,因为app.jspassport.js之前开始初始化express-session.js做错了,这是错误的!!

但是,正如其他人所提到的,这段代码无论如何都可以解决问

app.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('/');
    });
});

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

document也提到了

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

所以,通过..解决了我案例中的注销问题

app.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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.