node.js的用户身份验证库?


274

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

在编写身份验证库之前,我认为我会看看人们是否知道现有的库。通过Google搜索找不到任何明显的内容。

-Shreyas


为了提高可搜索性:与omniauth(rails)或python 等效的东西social-auth。PHP(和其他常见的Web服务器语言)用户也可以随意添加其等效项。
forivall

Answers:


233

如果您正在寻找适用于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等进行身份验证。如有必要,可插入自定义策略。


在节点的所有身份验证包中,我选择了护照。它有据可查且易于使用,并支持更多策略。
Tech-man

目前,我正在使用护照作为原型,但我不建议这样做,因为它似乎没有维护,而且设计也不是很好。例如,当它仅使用req.session.messages时,它会强制您使用connect-flash,并且由于它使用了已弃用的Google OpenId,并且网站上广告的passport-google已过时,并且没有指向google-oauth应该替换它。这也是经过身份验证后回调的签名:done(null,false,{ message:'Incorrect username.' })这很糟糕,因为我们不知道所有这些参数是什么。
eloone

1
@eloone我需要更新文档以指向Google现在更喜欢的新身份验证方法。正如您提到的那样,对这些人员的支持存在并且它们工作良好。对于设计问题,passport不会强迫您使用connect-flash,并且您提到的论据已记录在指南中。如果您需要帮助理解,可以在一些论坛上提供帮助,并回答您的问题。
Jared Hanson 2014年

并非一无所有-但我刚刚完成了Passport的插入(使用提供的示例)。超级容易!我知道距最近的评论已经过去了几年。我建议任何人看看。
terary

89

会话+如果

我想您尚未找到很多好的库的原因是,使用库进行身份验证大部分是经过精心设计的。

您正在寻找的只是会话绑定器:)具有以下内容的会话:

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这一非常强大且易于阅读的洋葱圈架构。不行-我的意见:)。您可以在此处找到有关连接如何工作以及洋葱圈构想的非常好的简短文章。

  2. 如果您按照书面要求,只想对数据库或文件使用基本或http登录。Connect-auth太大。适用于OAuth 1.0,OAuth 2.0&Co之类的东西


使用connect进行非常简单的身份验证

(它是完整的。只需执行它以进行测试,但是如果要在生产中使用它,请确保使用https)(并且要符合REST-Principle的要求,您应该使用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()吗?可以吗
jpstrikesback 2011年

3
是。它必须使用next(),因为那是连接背后的想法。Connect具有层架构/形式的代码结构。而且每一层都可以通过不调用next()来停止请求执行。如果我们正在谈论认证:认证层将检查用户是否具有正确的权限。如果一切正常,则该层调用next()。如果不是,则此auth层将生成错误,并且不会调用next()。
Matthias

男人,这正是我想要的。connect-auth让我有些消化不良。我是第一次登录我的应用程序。非常感谢。
安迪·雷

7
这仍然无助于回答如何连接到数据库后端(最好使用加密密码)。我感谢您的评论,认为这是一个过度设计的库,但肯定有一个不是。另外,如果我想编写自己的身份验证系统,我会在Java中使用Struts。就像OP一样,我想知道哪些插件将在1行代码中为我做到这一点。
hendrixski 2011年

4
好答案Nivoc。不适用于最新版本的connect tho。我必须更改... cookieDecoder()-> cookieParser()和bodyDecoder()-> bodyParser()并从helloWorldContent函数中删除next()调用,因为我遇到了一个错误“在它们之后不能设置标题发送”
迈克尔Dausmann

26

看起来连接中间件的connect-auth插件正是我需要的:http : //wiki.github.com/ciaranj/connect-auth/creating-a-form-b​​ased-strategy

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


1
嘿,你有一个例子吗?只需要求connect-auth并在“ req”上调用“ .authenticate”即可返回“ TypeError:对象#对我而言没有方法“ authenticate””。
米莎·雷兹林

1
恕我直言,这个插件是办法沉重简单的HTTP认证
马蒂亚斯

该插件可与连接洋葱圈架构配合使用
-Matthias

14

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

  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("/");
    }
  });
});

无论如何,这种方法主要是设计得灵活而简单。我敢肯定,有很多方法可以改善它。如果您有任何意见,我非常希望您提供反馈。

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


2
这很好,除了您应该使用bcrypt来存储密码(不是db中的纯文本)。这里有一个很好的帖子:devsmash.com/blog/…–
chovy


7

这是我的一个项目的一些基本身份验证代码。我将它与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之上)
shreddd 2010年

4

身份验证的另一种方式是无密码,这是一种基于令牌的用于快速表达的身份验证模块,它规避了密码的固有问题[1]。它实现起来很快,不需要太多表格,并且为普通用户提供了更好的安全性(完整披露:我是作者)。

[1]:密码已过时


3

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

那么与现有解决方案有什么区别?

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

看一下例子


2

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



1

使用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);
}

0

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

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

披露:我是该库的开发人员。


0

如果您需要使用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的作者。


0

甜认证

轻量级,零配置的用户身份验证模块。它不需要一个正确的数据库。

https://www.npmjs.com/package/sweet-auth

它很简单:

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
    }
})
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.