【问题标题】:Node.js Authentication library with "persistence token" functionality具有“持久性令牌”功能的 Node.js 身份验证库
【发布时间】:2012-11-28 05:45:17
【问题描述】:

我有 passport for node 的工作知识,但它没有以下内容:

  • 生成“持久性令牌”(例如在authlogic/session/session.rb#L35
  • 为密码重置生成易腐烂的令牌
  • 记住我的功能
  • 管理用于登录/注销某些模型类等的属性。

Node.js 社区中是否有解决此问题的库?如果有任何东西像 Devise for Rails 一样健壮(或正在变得健壮),那将是完美的,但任何解决这个令牌问题的东西都可以正常工作。

疯狂的是,很多示例都将用户 id 存储在会话中!

request.session['userId'] = user.get('id')

这只是要求被黑客入侵。

应该是这样的:

require.session['persistenceToken'] = App.User.generateRandomToken()

【问题讨论】:

标签: javascript node.js authentication


【解决方案1】:

我将实施一种用于密码重置的一次性密码(又名一次性令牌)策略。鉴于 Passport 的架构,这很容易成为一个单独的模块,如果发现其他人已经实现了这样的东西,我不会感到惊讶。

记住我,持久性令牌功能也是我想要支持的。最好,我也希望这也是一个单独的策略,但它可能需要一些核心支持。如果是这样的话,req.logIn (https://github.com/jaredhanson/passport/blob/master/lib/passport/http/request.js#L28) 中的几行额外的行应该能够覆盖它。

至于在会话中存储用户 ID,我认为没有什么大的风险,因为默认情况下,在 Connect/Express 中,会话属性完全存储在后端并由唯一的 sid 查找,即设置在加密的 cookie 中。恶意用户必须同时拥有唯一的 sid 和会话密钥才能欺骗请求。

Mozilla 正在使用 Passport 作为其身份验证工作的一部分,以将 BrowserID 连接到其他缺乏 BrowserID 支持的提供商(请参阅browserid-bigtent)。鉴于他们的要求,开发人员可以确保 Passport 符合严格的安全要求。

(最终,Passport 中的会话序列化是应用程序的责任,因此如果出于安全原因需要,可以使用随机令牌代替用户 ID。显然,如果将数据直接存储在 cookie 中,则应该这样做,但我会建议这样做是最不明智的做法。)

就管理模型上的这些属性而言,Passport 被设计为完全与模型/ORM 无关。我不打算改变这个事实,因为我认为这些决定最好留给应用程序(并且由于委派了这种责任,Passport 更加灵活)。也就是说,我认为其他模块还有空间独立构建在 Passport 之上以提供此功能。

综上所述,我认为 Passport 是现有 Node.js 身份验证解决方案中最强大的。您的前三个请求将大大有助于使其变得更加如此,并且它们应该很容易完成。我很乐意合作开发这些功能,因此请随时与我联系。

最后,如果有人好奇,一流的 API 身份验证目前正在工作中,在 authinfo 分支上。在此基础上,passport-http-oauth 实现了 OAuth 服务器策略,可以与 oauthorize middlware 结合作为工具包来组装 OAuth 服务器。这还没有完全成熟,但是当它准备好时,它将成为 Passport 的另一个有效功能。

【讨论】:

  • 我正在使用带本地身份验证策略的护照,并希望添加“记住我”功能。我可以以某种方式在我的 serializeUser() 函数中设置 cookie 生存期吗?我必须破解核心吗?还是别的什么?
【解决方案2】:

与此同时,以下内容就足够了。根据需要进行调整以适合您的应用程序和所需的 cookie 持续时间。检查用户名和密码是检测登录尝试的一种俗气的方法,但它与护照一起工作得很好。

app.use(function(req, res, next) {
    if(
            typeof(req.body.username) !== "undefined"
            && typeof(req.body.password) !== "undefined"
            ) {
        if(req.body.remember == 1) {
            req.session.cookie.maxAge = 365*24*60*60*1000;
        }
        else {
            req.session.cookie.maxAge = 24*60*60*1000;
        }
    }
    next();
});

【讨论】:

  • 我想知道为什么在else 语句中设置res.session.cookie.maxAge = null; 不起作用?我试过了,如果我在“记住我”选项设置为 true 的情况下登录一次,那么即使“记住我”已关闭,所有后续登录也会设置持久 cookie。
  • 我刚刚意识到我应该改用req.session.cookie.expires = false,我将发布这个问题的替代答案。
【解决方案3】:

虽然我肯定希望在 passport.js 中看到这些功能,但它们还没有。

我创建了一个简单的随机令牌生成器以与 passport.js serilizeUser() 函数一起使用,并稍微修改了 Justen 的答案以满足我的需要。基本上,唯一的区别是如果没有设置“记住”选项,只要浏览器打开,会话就会持续。

这是我的带有随机访问令牌生成器的序列化程序。我正在使用 Mongodb 和 Mongoose,但实现应该可以很好地转换到其他系统。

基本上,我正在获取时间并向其附加一个随机的 16 个字符的字符串。然后,在 serializeUser() 函数中,我检查没有其他用户具有相同的令牌(令牌应该是唯一的!)。

User.methods.generateRandomToken = function () {
  var user = this,
      chars = "_!abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
      token = new Date().getTime() + '_';
  for ( var x = 0; x < 16; x++ ) {
    var i = Math.floor( Math.random() * 62 );
    token += chars.charAt( i );
  }
  return token;
}; 

这是序列化程序:

passport.serializeUser( function ( user, done ) {

  var createAccessToken = function () {
    var token = user.generateRandomToken();
    app.User.findOne( { accessToken: token }, function (err, existingUser) {
      if (err) return done( err );
      if (existingUser)
        createAccessToken(); // Run the function again - the token has to be unique!
      else {
        user.set( 'accessToken', token );
        user.save( function ( err ) {
          if (err) return done( err );
          return done( null, user.get('accessToken') );
        })
      }
    });
  };

  if ( user._id ) {
    createAccessToken();
  }
});

...这是我处理“记住我”功能的中间件版本。不过,我更希望这以某种方式成为 serializeUser 函数或 passport.js 核心的一部分。

app.use( express.session( { secret: 'secret_key' } ) );
app.use( function (req, res, next) {
    if ( req.method == 'POST' && req.url == '/login' ) {
      if ( req.body.remember ) {
        req.session.cookie.maxAge = 30*24*60*60*1000; // Rememeber 'me' for 30 days
      } else {
        req.session.cookie.expires = false;
      }
    }
    next();
});
app.use( passport.initialize() );
app.use( passport.session() );

我希望能有所帮助。我花了几个小时才弄明白,我不太确定这是最好的方法,但它现在对我有用。

【讨论】:

  • 我知道这真的很老了,但是在这里使用会话中间件有什么帮助?我们不能跳过它吗?
猜你喜欢
  • 1970-01-01
  • 2021-02-21
  • 1970-01-01
  • 2017-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-27
相关资源
最近更新 更多