护照登录与会话持久化

52

背景

我有一个具备CRUD功能的MEAN应用程序,并已经使用Postman进行了全面测试。我一直在尝试将登录信息持久化,但一直没有成功。我已经阅读并尝试了以下内容:

但我只能注册并登录用户,无法使用会话持久化登录。

我的应用程序

这里是完整的Github repo链接(如果你想查看最新更改,请查看develop分支)

我对身份验证/登录的理解

这里是我的用户登录理解,包括来自项目的代码示例以及Postman结果和控制台日志的屏幕截图。

Passport设置

我有以下auth.js文件,它配置了护照。

var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;

module.exports = function(app, user){

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

  // passport config
  passport.use(new LocalStrategy(user.authenticate()));

  passport.serializeUser(function(user, done) {
    console.log('serializing user: ');
    console.log(user);
    done(null, user._id);
  });

  passport.deserializeUser(function(id, done) {
    user.findById(id, function(err, user) {
      console.log('no im not serial');
      done(err, user);
    });
  });
};

这个函数在服务器文件中被调用,如下所示:

//code before
var user    = require('./models/user.js');
var auth    = require('./modules/auth.js')(app, user);
// code after

登录路由

在我的路由中,我有以下登录路由

router.post('/login', function(req, res, next) {

  passport.authenticate('local', function(err, user, info) {

    if (err) {
        return next(err);
    }

    if (!user) {
        return res.status(401).json({
            err: info
        });
    }

    req.logIn(user, function(err) {

        if (err) {
            return res.status(500).json({
                err: 'Could not log in user'
            });
        }

        res.status(200).json({
            status: 'Login successful!'
        });

    });
  })(req, res, next);
});

这条路由已经在Postman中测试过,可以正常使用。我输入了用户名和密码并获得了以下响应。

enter image description here

当访问此路由时,我们还可以在控制台中看到用户的序列化。

enter image description here

接下来该怎么办?

这就是我困惑的地方。我有一些问题。

  1. 用户现在是否在我的服务器上的会话中?
  2. 我应该将 req.session.passport.user 发送回客户端吗?
  3. 所有未来请求都需要会话ID吗?

测试会话

我设置了第二个路由以测试会话,如下所示:

router.get('/checkauth', passport.authenticate('local'), function(req, res){

    res.status(200).json({
        status: 'Login successful!'
    });

});

我认为,passport.authenticate('local') 这一部分的作用是在允许通过路由之前测试用户会话是否存在,但即使在登录后运行它,我仍然无法获得 200 响应。

这个路由是否需要在头部传递 req.session.passport.user 或者在需要身份验证的 http 请求的数据参数中传递?

如果我漏掉了什么或者对某些事情的理解有误,请告诉我,非常感谢你们的帮助。

4个回答

80

用户是否在我的服务器上的会话中?

不是,你需要在app.use(passport.session());之前使用express-session中间件来实际存储会话在内存/数据库中。该中间件负责将cookie设置到浏览器中,并将浏览器发送的cookie转换为req.session对象。PassportJS仅使用该对象进一步反序列化用户。

我应该将req.session.passport.user返回给客户端吗?

如果您的客户端期望登录后返回一个user资源,则应该返回。否则,我看不到将用户对象发送到客户端的任何理由。

我需要在所有未来的请求中都传递会话ID吗?

是的,对于所有未来的请求,都需要会话ID。但是,如果您的客户端是浏览器,则无需发送任何内容。浏览器将将会话ID存储为cookie,并在cookie过期之前发送它以进行所有后续请求。express-session将读取该cookie并将相应的会话对象附加为req.session

测试会话

passport.authenticate('local')用于从POST主体验证用户凭据。您应该仅在登录路由中使用此选项。

但是,要检查用户在所有其他路由中是否经过身份验证,可以检查req.user是否已定义。

function isAuthenticated = function(req,res,next){
   if(req.user)
      return next();
   else
      return res.status(401).json({
        error: 'User not authenticated'
      })

}
router.get('/checkauth', isAuthenticated, function(req, res){

    res.status(200).json({
        status: 'Login successful!'
    });
});

谢谢Hassansin,我的应用程序现在的会话功能已经可以使用了。 - Joe Lloyd
1
如果我在每个路由中使用req.isAuthenticated()而不是req.user来检查用户是否已登录,会发生什么? - Bharat Chhabra
1
那么,在 Node 应用程序重新启动后如何保持用户登录状态呢?因为现在它只是一个内存会话。 - StErMi
@hassansin,使用Postman登录后我的登录状态保持了下来,但是在浏览器中却没有。我正在使用Angular。我需要怎么做才能让登录状态在Angular的/login路由之后仍然保持? - Matheus Bernardi

17

正如@hassansin所说,您需要使用实现会话管理的中间件。passport.session()中间件是将passport框架连接到会话管理中,并不会自己实现会话。您可以使用express-session中间件来实现会话管理。您需要以下面的方式修改您的auth.js

var passport = require('passport');
var session = require('express-session');
var LocalStrategy = require('passport-local').Strategy;

module.exports = function(app, user){
  app.use(session({secret: 'some secret value, changeme'}));    

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

  // passport config
  passport.use(new LocalStrategy(user.authenticate()));

  passport.serializeUser(function(user, done) {
    console.log('serializing user: ');
    console.log(user);
    done(null, user._id);
  });

  passport.deserializeUser(function(id, done) {
    user.findById(id, function(err, user) {
      console.log('no im not serial');
      done(err, user);
    });
  });
};

请注意,这种情况下会使用内存存储的会话引擎,如果您扩展应用程序并应用负载平衡,则无法正常工作。当您达到这个开发状态时,需要类似于connect-redis会话存储

还要注意,您需要更改会话中间件调用所使用的密钥值,并在所有应用程序实例中使用相同的值。


4
根据passport文档req.user将被设置为已验证的用户。但是,为了让它正常工作,您需要使用express-session模块。除了已经拥有的东西以外,您不需要其他任何东西来使passport工作。
就会话测试而言,您可以拥有一个中间件函数来检查req.user是否设置,如果设置,则我们知道该用户已通过身份验证,如果未设置,则可以将用户重定向。
例如,您可以拥有一个中间件函数,可以在任何需要身份验证的路由上使用。 authenticated.js
module.exports = function (req, res, next) {
    // if user is authenticated in the session, carry on
    if (req.user) {
        next();
    }
    // if they aren't redirect them to the login page
    else {
        res.redirect('/login');
    }
};

控制器

var authenticated = require('./authenticated');

router.get('/protectedpage', authenticated, function(req, res, next) {
    //Do something here
});

0

我不知道有没有一种方法可以检查所有现有的会话,但 Passport 处理会话 ID 的发放。尝试在登录后检查测试端点上是否有 req.user。


没有正常工作的部分是“测试会话”部分。我登录了,但无法发出检查授权的请求。但是如果我不发送会话ID,路由如何知道我是正确的用户呢?有没有办法查看服务器上活动的会话? - Joe Lloyd

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