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

时间:2012-12-07 06:42:21

标签: javascript node.js express logout passport.js

我无法让我的系统使用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();
}

23 个答案:

答案 0 :(得分: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 :(得分:41)

陷入同样的​​问题。使用req.session.destroy();代替req.logout();有效,但我不知道这是否是最佳做法。

答案 2 :(得分: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. Logout req加载会话
  5. 退出请求会破坏会话
  6. Logout req将重定向发送到浏览器(不删除cookie)
  7. Req 1完成处理
  8. Req 1将会话从内存保存到redis
  9. 用户打开没有登录对话框的页面,因为cookie和会话都已到位
  10. 理想情况下,您需要对api调用使用令牌身份验证,并且只使用仅加载页面的Web应用程序中的会话,但即使您的Web应用程序仅用于获取api令牌,这种竞争条件仍然存在。

答案 3 :(得分:7)

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

<强>(坏)

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

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

<强>(良好)

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

答案 4 :(得分:5)

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

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

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

答案 5 :(得分:4)

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

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

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

答案 6 :(得分: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也提到

  

请注意,启用会话支持完全是可选的,尽管它是   推荐用于大多数应用。如果启用,请务必使用   express.session()在passport.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();正在运作。

答案 7 :(得分: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('/')
})

答案 8 :(得分: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方法。

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

答案 9 :(得分: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');
});

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

答案 10 :(得分:0)

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

    npm install --save express@3.0.0

答案 11 :(得分: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本质上是异步的

答案 12 :(得分:0)

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

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

通过升级到Passport 0.4.0并添加行

解决了这个问题
app.get('/logout', function(req, res) {
    req.logOut();
    res.redirect('/');
});

答案 13 :(得分:0)

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

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


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

答案 14 :(得分: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
})

答案 15 :(得分:0)

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

我通过不执行重定向而是返回了json消息来解决了该问题。

答案 16 :(得分:0)

只需添加req.logOut();解决了这个问题; “ O”应大写

答案 17 :(得分:0)

这仍然是个问题。

我所做的是每当他们注销时,在服务器端和客户端使用req.session.destroy(function (err) {});

const logout = () => {
    const url = '/users/logout'
    fetch(url)
    setTimeout(function () {
      location.reload();    }, 500);

这样,刷新页面时,用户将没有会话。如果没有人通过身份验证,只需确保您重定向到正确的页面即可。

也许不是最好的方法,但是它可行。

答案 18 :(得分:0)

您可以尝试手动重新生成会话:

app.get('/logout', (req, res) => {
    req.logOut();
    req.session.regenerate(err => {
        err && console.log(err);
    });
    res.redirect('/');
});

这不会从会话中删除其他数据(例如护照)。

答案 19 :(得分:0)

试试这个

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

答案 20 :(得分:-1)

您应该使用req.logout()破坏浏览器中的会话。

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

答案 21 :(得分:-3)

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

答案 22 :(得分:-3)

在我的情况下,使用传递给req.session.destroy的回调只在某些时候有所帮助,我不得不求助于这个黑客攻击:

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

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

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

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

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