【问题标题】:user authentication libraries for node.js?node.js 的用户身份验证库?
【发布时间】:2011-03-30 16:57:13
【问题描述】:

是否有任何现有的 node.js 用户身份验证库?特别是,我正在寻找可以为用户进行密码身份验证的东西(使用自定义后端身份验证数据库),并将该用户与会话相关联。

在我写一个 auth 库之前,我想我会看看人们是否知道现有的库。通过谷歌搜索找不到任何明显的东西。

-Shreyas

【问题讨论】:

  • 对于可搜索性:相当于 omniauth (rails) 或 python social-auth。 PHP(和其他常见的 Web 服务器语言)用户也应该随意添加它们的等价物。

标签: authentication node.js serverside-javascript


【解决方案1】:

如果您正在寻找 Connect 或 Express 的身份验证框架,Passport 值得研究:https://github.com/jaredhanson/passport

(披露:我是 Passport 的开发者)

我在调查了 connect-auth 和everyauth 之后开发了 Passport。虽然它们都是很棒的模块,但它们并不适合我的需求。我想要更轻巧且不引人注目的东西。

Passport 被分解为单独的模块,因此您可以选择仅使用您需要的内容(OAuth,仅在必要时使用)。 Passport 也不会在您的应用程序中安装任何路由,让您可以灵活地决定何时何地进行身份验证,并使用挂钩来控制身份验证成功或失败时发生的情况。

例如,这里是设置基于表单(用户名和密码)身份验证的两步过程:

passport.use(new LocalStrategy(
  function(username, password, done) {
    // Find the user from your DB (MongoDB, CouchDB, other...)
    User.findOne({ username: username, password: password }, function (err, user) {
      done(err, user);
    });
  }
));

app.post('/login', 
  passport.authenticate('local', { failureRedirect: '/login' }),
  function(req, res) {
    // Authentication successful. Redirect home.
    res.redirect('/');
  });

其他策略可用于通过 Facebook、Twitter 等进行身份验证。如有必要,可以插入自定义策略。

【讨论】:

  • 在节点的所有身份验证包中,我选择了护照。它有据可查且易于使用,并支持更多策略。
  • 我现在用passport做原型,不推荐,因为好像没维护,设计也不是很好。例如,当它可以简单地使用 req.session.messages 时,它会强制您使用 connect-flash,并且网站上宣传的 passport-google 已过时,因为它使用已弃用的 Google OpenId,并且没有指向护照的链接- google-oauth 应该替换它。这也是身份验证后回调的签名:done(null,false,{ message:'Incorrect username.' }) 这很糟糕,因为我们不知道所有这些参数是什么。
  • @eloone 我需要更新文档以指向 Google 现在更喜欢的新身份验证方法。正如您所提到的,这些支持存在并且它们运行良好。至于设计问题,passport 不会强迫你使用 connect-flash,你提到的论点都记录在指南中。如果您需要帮助理解,可以在论坛上提供帮助并回答您的问题。
  • 并非没有 - 但我刚刚完成了 Passport 的插入(使用了提供的示例)。超级简单!我意识到距离最近的 cmets 已经有几年了。我建议大家看看。
  • 我可以使用passport js创建自己的身份验证服务并使用我为我的ASP .net核心应用程序和其他应用程序创建的服务吗?
【解决方案2】:

会话 + 如果

我猜你没有找到很多好的库的原因是使用库进行身份验证大多是过度设计的。

您正在寻找的只是一个会话绑定器 :) 一个会话:

if login and user == xxx and pwd == xxx 
   then store an authenticated=true into the session 
if logout destroy session

就是这样。


我不同意你的结论,即 connect-auth 插件是要走的路。

我也在使用 connect,但我不使用 connect-auth 有两个原因:

  1. 恕我直言,connect-auth 打破了连接的非常强大且易于阅读的洋葱圈架构。不行-我的意见:)。 您可以找到一篇关于连接如何工作和洋葱圈想法here 的非常好的简短文章。

  2. 如果您 - 如所写 - 只想对数据库或文件使用基本或 http 登录。 Connect-auth 太大了。它更适用于 OAuth 1.0、OAuth 2.0 和 Co


一个非常简单的连接认证

(已经完成。执行它进行测试,但如果你想在生产中使用它,请确保使用https) (并且要符合 REST 原则,您应该使用 POST-Request 而不是 GET-Request b/c 您更改状态:)

var connect = require('connect');
var urlparser = require('url');

var authCheck = function (req, res, next) {
    url = req.urlp = urlparser.parse(req.url, true);

    // ####
    // Logout
    if ( url.pathname == "/logout" ) {
      req.session.destroy();
    }

    // ####
    // Is User already validated?
    if (req.session && req.session.auth == true) {
      next(); // stop here and pass to the next onion ring of connect
      return;
    }

    // ########
    // Auth - Replace this example with your Database, Auth-File or other things
    // If Database, you need a Async callback...
    if ( url.pathname == "/login" && 
         url.query.name == "max" && 
         url.query.pwd == "herewego"  ) {
      req.session.auth = true;
      next();
      return;
    }

    // ####
    // This user is not authorized. Stop talking to him.
    res.writeHead(403);
    res.end('Sorry you are not authorized.\n\nFor a login use: /login?name=max&pwd=herewego');
    return;
}

var helloWorldContent = function (req, res, next) {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('authorized. Walk around :) or use /logout to leave\n\nYou are currently at '+req.urlp.pathname);
}

var server = connect.createServer(
      connect.logger({ format: ':method :url' }),
      connect.cookieParser(),
      connect.session({ secret: 'foobar' }),
      connect.bodyParser(),
      authCheck,
      helloWorldContent
);

server.listen(3000);

注意

我在一年前写了这个声明,目前没有活动的节点项目。因此 Express 中可能存在 API 更改。如果我应该更改任何内容,请添加评论。

【讨论】:

  • 为什么 connect-auth 会破坏洋葱/层模式?是因为它不使用next()吗?可以吗?
  • 是的。它必须使用 next() 因为这就是连接背后的想法。 Connect 具有层架构/代码结构形式。并且每一层都有权通过不调用 next() 来停止请求执行。如果我们在谈论身份验证:身份验证层将检查用户是否具有正确的权限。如果一切正常,该层调用 next()。如果不是,这个 auth-layer 会产生一个错误并且不会调用 next()。
  • 伙计,这正是我想要的。 connect-auth 让我有点消化不良。我刚刚第一次登录我的应用程序。非常感谢。
  • 这仍然无助于回答如何连接到数据库后端(最好使用加密密码)。我很欣赏你的评论,即这个库被过度设计了,但肯定有一个不是。此外,如果我想编写自己的身份验证系统,我会在 Java 中使用 Struts。就像 OP 一样,我想知道哪些插件会在 1 行代码中为我做到这一点。
  • 很好的答案 Nivoc。不适用于最新版本的 connect tho。我不得不更改... cookieDecoder() --> cookieParser() 和 bodyDecoder() --> bodyParser() 并从 helloWorldContent 函数中删除 next() 调用,因为我收到错误“无法在它们之后设置标题”已发送'
【解决方案3】:

看来连接中间件的connect-auth 插件正是我所需要的

我使用的是 express [ http://expressjs.com ],所以 connect 插件非常适合,因为 express 是 connect 的子类(好的 - 原型)

【讨论】:

  • 嘿,你有你做过的例子吗?只需要 connect-auth 并在“req”上调用“.authenticate”返回“TypeError: Object # has no method 'authenticate'”。
  • 恕我直言,这个插件对于简单的 http 身份验证来说太重了
  • 这个插件适用于连接洋葱圈架构
【解决方案4】:

我基本上在寻找同样的东西。具体来说,我想要以下内容:

  1. 使用 express.js,它封装了 Connect 的中间件功能
  2. “基于表单”的身份验证
  3. 对哪些路由进行身份验证进行精细控制
  4. 用户/密码的数据库后端
  5. 使用会话

我最终做的是创建自己的中间件函数check_auth,我将其作为参数传递给我想要验证的每个路由。 check_auth 仅检查会话,如果用户未登录,则将其重定向到登录页面,如下所示:

function check_auth(req, res, next) {

  //  if the user isn't logged in, redirect them to a login page
  if(!req.session.login) {
    res.redirect("/login");
    return; // the buck stops here... we do not call next(), because
            // we don't want to proceed; instead we want to show a login page
  }

  //  the user is logged in, so call next()
  next();
}

然后对于每个路由,我确保这个函数作为中间件传递。例如:

app.get('/tasks', check_auth, function(req, res) {
    // snip
});

最后,我们需要实际处理登录过程。这很简单:

app.get('/login', function(req, res) {
  res.render("login", {layout:false});
});

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

  // here, I'm using mongoose.js to search for the user in mongodb
  var user_query = UserModel.findOne({email:req.body.email}, function(err, user){
    if(err) {
      res.render("login", {layout:false, locals:{ error:err } });
      return;
    }

    if(!user || user.password != req.body.password) {
      res.render("login",
        {layout:false,
          locals:{ error:"Invalid login!", email:req.body.email }
        }
      );
    } else {
      // successful login; store the session info
      req.session.login = req.body.email;
      res.redirect("/");
    }
  });
});

无论如何,这种方法主要是为了灵活和简单而设计的。我相信有很多方法可以改进它。如果您有任何意见,我非常希望收到您的反馈。

编辑:这是一个简化的例子。在生产系统中,您永远不想以纯文本形式存储和比较密码。正如评论者指出的那样,有一些库可以帮助管理密码安全。

【讨论】:

  • 这很好,除了你应该使用 bcrypt 来存储密码(不是数据库中的纯文本)。这里有一篇关于它的好帖子:devsmash.com/blog/…
【解决方案5】:

如果您想要第三方/社交网络登录集成,请查看everyauth

【讨论】:

    【解决方案6】:

    这是我的一个项目中用于基本身份验证的一些代码。我在 CouchDB 上使用它并带有额外的身份验证数据缓存,但我删除了该代码。

    围绕您的请求处理包装一种身份验证方法,并为不成功的身份验证提供第二个回调。成功回调将获取用户名作为附加参数。不要忘记在失败回调中正确处理带有错误或缺少凭据的请求:

    /**
     * Authenticate a request against this authentication instance.
     * 
     * @param request
     * @param failureCallback
     * @param successCallback
     * @return
     */
    Auth.prototype.authenticate = function(request, failureCallback, successCallback)
    {
        var requestUsername = "";
        var requestPassword = "";
        if (!request.headers['authorization'])
        {
            failureCallback();
        }
        else
        {
            var auth = this._decodeBase64(request.headers['authorization']);
            if (auth)
            {
                requestUsername = auth.username;
                requestPassword = auth.password;
            }
            else
            {
                failureCallback();
            }
        }
    
    
        //TODO: Query your database (don't forget to do so async)
    
    
        db.query( function(result)
        {
            if (result.username == requestUsername && result.password == requestPassword)
            {
                successCallback(requestUsername);
            }
            else
            {
                failureCallback();
            }
        });
    
    };
    
    
    /**
     * Internal method for extracting username and password out of a Basic
     * Authentication header field.
     * 
     * @param headerValue
     * @return
     */
    Auth.prototype._decodeBase64 = function(headerValue)
    {
        var value;
        if (value = headerValue.match("^Basic\\s([A-Za-z0-9+/=]+)$"))
        {
            var auth = (new Buffer(value[1] || "", "base64")).toString("ascii");
            return {
                username : auth.slice(0, auth.indexOf(':')),
                password : auth.slice(auth.indexOf(':') + 1, auth.length)
            };
        }
        else
        {
            return null;
        }
    
    };
    

    【讨论】:

    • 我想避免基本身份验证,而支持基于表单的身份验证。这绝对是基本身份验证问题的优雅解决方案。我想我可能已经找到了一个很好的身份验证框架(connect-auth - 位于 connectjs 之上)
    【解决方案7】:

    Passwordless 是一种不同的身份验证方式,它是一个用于 express 的 token-based authentication 模块,可以规避密码的固有问题 [1]。它实施起来很快,不需要太多的表单,并且为普通用户提供了更好的安全性(完全披露:我是作者)。

    [1]:Passwords are Obsolete

    【讨论】:

      【解决方案8】:

      几年过去了,我想介绍一下我的 Express 身份验证解决方案。它被称为 Lockit。您可以在 GitHub 找到该项目,并在 my blog 找到简短介绍。

      那么与现有解决方案有什么不同?

      • 易于使用:设置您的数据库、npm 安装、require('lockit')lockit(app),完成
      • 已内置路由(/signup、/login、/forgot-password 等)
      • 已内置视图(基于 Bootstrap,但您可以轻松使用自己的视图)
      • 它支持您的 AngularJS / Ember.js 单页应用程序的 JSON 通信
      • 它不支持 OAuth 和 OpenID。只有usernamepassword
      • 它可以与多个开箱即用的数据库(CouchDB、MongoDB、SQL)一起使用
      • 它有测试(我找不到任何石膏板测试)
      • 积极维护(与everyauth 相比)
      • 电子邮件验证和忘记密码过程(发送带有令牌的电子邮件,Passport 不支持)
      • 模块化:只使用你需要的东西
      • 灵活性:自定义所有内容

      看看examples

      【讨论】:

        【解决方案9】:

        有一个名为Drywall 的项目使用Passport 实现了用户登录系统,并且还有一个用户管理管理面板。如果您正在寻找一个功能齐全的用户身份验证和管理系统,类似于 Django 所拥有的,但适用于 Node.js,就是这样。我发现它是构建需要用户身份验证和管理系统的节点应用程序的一个非常好的起点。有关 Passport 工作原理的信息,请参阅 Jared Hanson's answer

        【讨论】:

          【解决方案10】:

          这里有两个流行的用于 node js 身份验证的 Github 库:

          https://github.com/jaredhanson/passport(建议)

          https://nodejsmodules.org/pkg/everyauth

          【讨论】:

            【解决方案11】:

            关于手动方法的注意事项:

            我很失望地看到,这篇博文中的一些建议代码示例不能防止会话固定或定时攻击等基本身份验证漏洞。

            与这里的一些建议相反,身份验证简单,并且手动解决方案并不总是微不足道的。我会推荐passportjsbcrypt

            如果您确实决定手动解决方案,请查看express js provided example 以获取灵感。

            祝你好运。

            【讨论】:

              【解决方案12】:

              使用 mongo 的快速简单示例,用于为 Angular 客户端提供用户身份验证的 API

              在 app.js 中

              var express = require('express');
              var MongoStore = require('connect-mongo')(express);
              
              // ...
              
              app.use(express.cookieParser());
              // obviously change db settings to suit
              app.use(express.session({
                  secret: 'blah1234',
                  store: new MongoStore({
                      db: 'dbname',
                      host: 'localhost',
                      port: 27017
                  })
              }));
              
              app.use(app.router);
              

              你的路线是这样的:

              // (mongo connection stuff)
              
              exports.login = function(req, res) {
              
                  var email = req.body.email;
                  // use bcrypt in production for password hashing
                  var password = req.body.password;
              
                  db.collection('users', function(err, collection) {
                      collection.findOne({'email': email, 'password': password}, function(err, user) {
                          if (err) {
                              res.send(500);
                          } else {
                              if(user !== null) {
                                  req.session.user = user;
                                  res.send(200);
                              } else {
                                  res.send(401);
                              }
                          }
                      });
                  });
              };
              

              然后在需要身份验证的路由中,您只需检查用户会话:

              if (!req.session.user) {
                  res.send(403);
              }
              

              【讨论】:

                【解决方案13】:

                这是一个使用时间戳记令牌的新身份验证库。令牌可以通过电子邮件或短信发送给用户,而无需将它们存储在数据库中。它可用于无密码身份验证或双因素身份验证。

                https://github.com/vote539/easy-no-password

                披露:我是这个库的开发者。

                【讨论】:

                  【解决方案14】:

                  如果您需要使用 Microsoft Windows 用户帐户通过 SSO(单点登录)进行身份验证。你可以试试https://github.com/jlguenego/node-expose-sspi

                  它将为您提供一个 req.sso 对象,其中包含所有客户端用户信息(登录名、显示名称、sid、组)。

                  const express = require("express");
                  const { sso, sspi } = require("node-expose-sspi");
                  
                  sso.config.debug = false;
                  
                  const app = express();
                  
                  app.use(sso.auth());
                  
                  app.use((req, res, next) => {
                    res.json({
                      sso: req.sso
                    });
                  });
                  
                  app.listen(3000, () => console.log("Server started on port 3000"));
                  

                  免责声明:我是 node-expose-sspi 的作者。

                  【讨论】:

                    【解决方案15】:

                    瘦身验证

                    轻量级、零配置的用户身份验证模块。它不需要单独的数据库。

                    https://www.npmjs.com/package/slimauth

                    很简单:

                    app.get('/private-page', (req, res) => {
                    
                        if (req.user.isAuthorized) {
                            // user is logged in! send the requested page
                            // you can access req.user.email
                        }
                        else {
                            // user not logged in. redirect to login page
                        }
                    })
                    

                    【讨论】:

                    • 模块已弃用。
                    猜你喜欢
                    • 2011-08-10
                    • 2018-02-27
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2016-04-12
                    • 1970-01-01
                    相关资源
                    最近更新 更多