护照登录和持久会话

Posted

技术标签:

【中文标题】护照登录和持久会话【英文标题】:Passport login and persisting session 【发布时间】:2016-07-28 22:30:33 【问题描述】:

背景

我有一个带有 CRUD 功能的 MEAN 应用程序,并已通过邮递员进行了全面测试。我一直在尝试坚持登录很长一段时间,但没有运气。我已经阅读并尝试了以下内容

passport docs toon io's blog about login Express session Scotch io, node auth made easy 加上大量其他轻量级阅读材料(很多 SO 问题)

但我只能注册和登录用户,不能通过会话保持登录。

我的应用

Here 是完整 github 存储库的链接(如果您正在寻找最新的更改,请查看开发分支)

我对 Auth/Login 的理解

这是我对用户登录的理解,其中包含我项目中的代码示例和邮递员结果的屏幕截图以及控制台日志。

护照设置

我有以下 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);
);

这条路线工作 经邮递员测试。我输入详细信息 'joe' 和 'pass' 并得到以下响应。

当这条路由被命中时,我们也可以在控制台中看到用户被序列化了。

那么接下来呢?

这是我迷路的地方。我有几个问题。

    用户现在是否在我的服务器上进行会话? 我应该将req.session.passport.user 发回给客户吗? 以后的所有请求都需要会话 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 请求的数据参数?

如果我遗漏了任何内容或理解有误,请告诉我,我们将不胜感激。谢谢大家。

【问题讨论】:

【参考方案1】:

用户现在是否在我的服务器上进行会话?

不,您需要在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,会话现在正在我的应用程序上运行。 如果我在每条路由上使用 req.isAutheticated() 而不是 req.user 来检查用户是否登录会发生什么? 那么node应用重启后如何让用户保持登录状态呢?因为现在它只是一个内存会话 @hassansin 使用邮递员我的登录仍然存在,但是在浏览器上却没有。我正在使用角。我需要做什么才能在 Angular 上的 /login 路由之后保持登录?【参考方案2】:

正如@hassansin 所说,您需要使用实现会话管理的中间件。 passport.session() 中间件是将护照框架连接到会话管理的,不自己实现会话。您可以使用express-session middleware 来实现会话管理。您需要通过以下方式修改您的 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 session store 之类的东西。

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

【讨论】:

【参考方案3】:

根据passport documentation,req.user 将设置为经过身份验证的用户。但是,为了使其工作,您将需要 express-session 模块。除了您已经拥有的护照之外,您应该不需要任何其他东西来工作。

至于测试会话,你可以有一个中间件函数来检查 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
);

【讨论】:

【参考方案4】:

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

【讨论】:

不工作的部分是“测试会话”部分。我登录但无法向 checkauth 发出请求。但是,如果我不发送会话 ID,路由如何知道我是正确的用户?有没有办法查看服务器上哪些会话处于活动状态?

以上是关于护照登录和持久会话的主要内容,如果未能解决你的问题,请参考以下文章

会话不是持久的 [PASSPORT]

不在护照快递服务器上保存会话

尝试通过护照linkedin集成linkedin登录时“无法在会话中找到请求令牌”

使用护照 + JWT 或会话保护 GraphQL API? (举例)

护照谷歌身份验证非持久性

护照会话不存在