Node.Js + express + Passport:无法注销用户。

9
我为一个Web服务器编写了一个JS脚本,使用passport和digest策略进行身份验证。我没有使用会话,但是我尝试过使用会话,结果并没有改变。浏览器请求“/login”路由并显示内置的登录对话框。身份验证运行良好,但我无法让用户“注销”。问题似乎是浏览器记住了登录凭据,并自动重新发送它们。最终结果是用户必须完全关闭浏览器才能退出登录,但这对于此应用程序来说是个问题。我知道一定有方法可以告诉浏览器不要这样做,但我还没有弄清楚。

我想出了一个窍门,让浏览器再次显示登录对话框;强制身份验证函数返回false。但是,我还没有找到每个会话单独处理此项操作的方法。现在,如果一个人注销登录,所有人都会被注销。这不是可行的解决方案。

有没有人告诉我在这里做错了什么?我想知道的一件事是,当浏览器POST到/logout路由时,我是否返回了适当的响应(请参见结尾)。我返回res.json(""),但也许我应该发送不同的响应来告诉浏览器忘记会话的凭据?

以下是我的代码。非常感谢您提供任何见解。提前致谢。

T

var passport = require('passport'), 
    DigestStrategy = require('passport-http').DigestStrategy;

var express = require('express');
var app = express();
app.configure(function () {
    app.use(
        "/", //the URL throught which you want to access to you static content
        express.static('./www') //where your static content is located in your filesystem
    );
  app.use(express.cookieParser());
  app.use(express.bodyParser());
  app.use(express.session({ secret: 'keep moving forward' }));
  app.use(passport.initialize());
  app.use(passport.session());
  app.use(app.router);


});
app.listen(80); //the port you want to use


/**
 * CORS support.
 */

app.all('*', function(req, res, next){
  if (!req.get('Origin')) return next();
  // use "*" here to accept any origin
  // For specific domain, do similar: http://localhost'
  // Use an array for multiple domains, like [http://localhost', 'http://example.com' ]
  res.set('Access-Control-Allow-Origin', '*' );
  res.set('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
  res.set('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Authorization');
  next();
});


//
// Configure passport authentication
//


// Used to force the browser to display the login screen again.
var forceLogin = false;
passport.use(new DigestStrategy({ qop: 'auth' },
  function(username, done ) { 

  if ( !forceLogin )
  {
    return done(null, username, "nimda");
  }
  else
  {
    //
    // Forces the browser to request the user name again by returning a failure to its last request.
    //
    console.log ( "forcing user to log in"  );
    forceLogin = false;
    return done(null, false);
  }
));

passport.serializeUser(function(user, done) {

    console.log( "serialize user " + user.toString() );
    done(null, user.toString());
});

passport.deserializeUser(function(id, done) {
    console.log( "deserialize user " + id.toString() );
    done(null, id);
});

app.post('/login', passport.authenticate('digest', { session: true }),
  function(req, res) {
    console.log( "/login");
    res.header('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0');
    res.json({ id: req.user.id, username: req.user.username });
 }); 

app.post('/logout', function(req, res){

  req.logOut();

  // NOTE: Same results as req.logout
  //
  //  req.session.destroy(function (err) {
  //   res.redirect('/'); 
  //  });

  res.redirect("/");

  // flag to force a login
  forceLogin = true;

  console.log( "logout");

  // Is this the proper return to the browser?
  return res.json("");
});

使用全局变量 forceLogin 是一个非常糟糕的主意,因为它将在所有用户的所有请求之间共享。 - Leonid Beschastny
同意,就像我在原帖中提到的那样。这就是为什么我希望有一个更好的解决方案。 - user761576
应该是 app.get('/logout') 吧?http://passportjs.org/guide/logout/ - ashley
我不明白为什么app.post或app.get会有所区别。无论如何,我尝试了一下,结果都是一样的。 - user761576
app也是一个全局变量。因此,使用app.set/app.get与使用全局变量是一样的。res.session是存储特定于会话的数据的正确位置。 - Leonid Beschastny
我同意使用res.session,但是我需要以某种方式将res.session传递给护照认证函数。有什么办法可以做到这一点吗? - user761576
4个回答

7
你可以尝试以下方法:
req.session.destroy()
req.logout()
res.redirect('/')

如果您正在使用一些形式的会话存储来进行持久性会话 (例如Redis),保存会话的新状态非常重要。我在类似的设置中尝试了上述代码,对我来说似乎是有效的。


请参见https://dev59.com/F2Yr5IYBdhLWcg3wVY3J - Matt Browne
3
req.session.destroy() 会使得 session 对象变成 undefined。 - DarkLeafyGreen

5

服务器需要以某种方式通知web浏览器需要刷新登录信息(MD5摘要)。为此,您可以发送401错误代码,通常浏览器会显示登录弹出消息。

顺便说一下,所有这些与会话有关的技巧都是无用的,因为浏览器已经具有自动登录所需的所有信息。

因此,您可以尝试以下代码:

req.logout();
res.send("logged out", 401);

2
尝试使用passport-http注销,这个一行代码解决了我的问题。谢谢! - Gang Su

4

有点晚了,但我在谷歌上搜索答案时找到了这个帖子,但没有任何方法对我有用。最终,我解决了它,所以我想在这里发布解决方案,供将来可能阅读此内容的任何人参考。


基本上,我使用node.js、express、passport(本地)和rethinkdb作为我的存储,并且我遇到了req.logout();无法注销我的问题。

我的设置:

var express = require( 'express' );
var passport = require( 'passport' );
var session = require( 'express-session' );
var RDBStore = require( 'express-session-rethinkdb' )( session );

我尝试了很多东西,但都没有成功,所以最终我决定手动操作,通过自己从数据库中删除会话记录来实现目标。

以下是我使用的代码: app.get( '/logout', function ( req, res, next ) {

    if ( req.isUnauthenticated() ) {

        // you are not even logged in, wtf
        res.redirect( '/' );

        return;

    }

    var sessionCookie = req.cookies['connect.sid'];

    if ( ! sessionCookie ) {

        // nothing to do here
        res.redirect( '/' );

        return;

    }

    var sessionId = sessionCookie.split( '.' )[0].replace( 's:', '' );

    thinky.r.db( 'test' ).table( 'session' ).get( sessionId ).delete().run().then( function( result ) {

        if ( ! result.deleted ) {

            // we did not manage to find session for this user
            res.redirect( '/' );

            return;

        }

        req.logout();
        res.redirect( '/' );

        return;

    });

});

所以我希望这能帮助某些人 :)

太棒了!问一下如何设置passport-local与rethinkdb连接是否有点无礼?我在存储值和密码哈希等创建过程中遇到了困难。非常感谢! - ServerSideSkittles
我只是来这里发布这个确切答案的。我也最终手动完成了这个过程,并且正在使用rethinkdb作为我的会话存储,并且具有完全相同的逻辑。 - BarthesSimpson

0

res.redirect('/') 会结束响应,之后你就不能再调用 write()、end()、send()、json() 等方法了。

就像这样:

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

deserializeUser 应该返回一个用户而不是 ID:

passport.deserializeUser(function(id, done) {
    findUser(id, function(err, user) {
       if(err) {
          done(err)
       } else {
          done(null, user);
       }
    }
});

我进行了一次简短的讨论,意识到摘要用户名/密码存储在浏览器上(而不是服务器上),因此req.logout无法帮助清除它,没有办法从服务器上清除。你只需要关闭浏览器再重新打开就相当于退出登录。

您可以在cookie或会话中使用变量来标记会话为注销,但我认为我们不应该这样做,因为用户名/密码仍然在浏览器中。

这是摘要!


我已经进行了这些更正,但浏览器仍然无法“注销”。 - user761576
好的,你给了我一个想法。这似乎告诉浏览器我已经注销了:app.post('/logout', function(req, res){ req.logOut(); console.log( "logout"); res.send(401, "未授权"); }); - user761576
1
不,它没有,这意味着我的解决方案没有起作用。 - user761576

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接