75

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

25 回答 25

87

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

希望这可以帮助!

于 2013-10-02T08:36:49.860 回答
43

遇到同样的问题。使用req.session.destroy();代替req.logout();工作,但我不知道这是否是最佳做法。

于 2013-01-11T12:03:22.953 回答
15

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

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

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

否则可能会发生这种情况:

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

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

于 2015-11-18T17:49:30.517 回答
8

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

(坏的)

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

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

(好的)

fetch('/auth/logout', { credentials: 'same-origin' })
  .then([other stuff]);
于 2016-09-02T18:39:23.267 回答
7

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

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

Edit: this is no longer an issue.

于 2014-11-26T15:07:59.057 回答
6

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

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

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

于 2016-10-04T10:29:16.123 回答
5

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

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

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

基本上我只是从更改saveUninitialized为。这似乎解决了这个问题。resavetruefalse

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

app.get('/logout', function(req, res) {
    req.logout();
    res.redirect('/');
});
于 2016-09-22T19:11:47.837 回答
3

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

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('/')
})
于 2019-02-23T18:18:03.460 回答
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使用更新的会话调用方法。

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

于 2015-04-05T07:38:31.083 回答
1

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

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


app.get('/logout', function(req, res) {
  req.logout();
  res.redirect('/');
});
于 2018-06-11T12:25:47.187 回答
1

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

于 2020-07-19T16:55:48.467 回答
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.js开始初始化做错了,这是错误的!passport.jsexpress-session.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('/');
    });
});

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

文件还提到

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

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

应用程序.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();是工作。

于 2017-04-15T17:44:35.907 回答
0

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

    npm install --save express@3.0.0
于 2015-03-25T04:09:04.163 回答
0

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

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

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

于 2020-12-06T12:58:13.983 回答
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 本质上是异步的

于 2017-11-26T16:56:16.457 回答
0

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

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

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

app.get('/logout', function(req, res) {
    req.logOut();
    res.redirect('/');
});
于 2018-05-01T10:34:27.897 回答
0

这仍然是一个问题。

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

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

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

也许不是最好的方法,但它确实有效。

于 2020-10-11T21:37:14.287 回答
0

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

我通过不进行重定向而仅返回一条 json 消息来解决它。

于 2018-12-03T11:45:12.490 回答
0

withCredentials: true我通过将我的axios.post请求设置为注销路由解决了这个问题。我猜没有发送识别会话所需的凭据,因此 req.logOut() 没有效果(我还注意到req.user在注销路由上未定义,这是一个很大的线索)

于 2021-09-06T15:54:45.233 回答
0

由于您使用的是通过 cookie 使用它自己的会话的护照身份验证,因此connect.sid处理注销的最简单方法是让护照处理会话。

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

  return res.status(401) // Handle unauthenticated response
})
于 2018-06-20T10:42:17.787 回答
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');
});

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

于 2015-11-17T22:10:28.230 回答
0

尝试这个

    app.get('/logout', (req, res) => {
    req.logout();
    req.session.destroy();
    res.redirect('/');
    }
于 2021-02-01T17:28:48.573 回答
-1

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

app.get('/logout', function(req, res) {
    req.logout();
    res.redirect('/'); // whatever the route to your default page is
});
于 2018-10-31T06:40:53.323 回答
-3

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

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

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

这可能与我的实时登录页面使用 SSL 的事实有关(我无法在暂存站点或本地主机上重现该问题)。我的应用程序中可能还会发生其他事情;我正在使用 derby-passport 模块,因为我的应用程序使用的是Derby框架,因此很难隔离问题。

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

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

于 2014-06-24T04:10:47.483 回答
-3

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

于 2015-11-26T07:02:28.073 回答