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

在我编写一个认证库之前,我想看看人们是否知道现有的库。在谷歌上找不到明显的东西。

-Shreyas


下面是我的一个项目中的一些基本身份验证代码。我使用它来对付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;
    }

};

看起来连接中间件的连接认证插件正是我所需要的

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


会话+ If

我想,您没有找到很多好的库的原因是,使用库进行身份验证在很大程度上是过度设计的。

你要找的只是一个会话绑定器:)一个会话:

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

这是它。


我不同意你认为connect-auth插件是正确的选择的结论。

我也使用连接,但我不使用连接认证的原因有两个:

IMHO打破了connect-auth非常强大且易于阅读的洋葱环架构。不去——我的观点:)。 你可以在这里找到一篇关于connect如何工作和洋葱圈想法的非常好的简短文章。 如果你——如前所述——只是想使用数据库或文件的基本或http登录。Connect-auth太大了。它更适合OAuth 1.0、OAuth 2.0等


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

(这是完整的。只是执行测试,但如果你想在生产中使用它,请确保使用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);

NOTE

我在一年多前写了这个声明,目前没有活动节点项目。可能会有API-Changes in Express。如果我需要修改什么,请加上评论。


如果你想要第三方/社交网络登录集成,也要看看每个auth。


如果您正在寻找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等进行身份验证还可以使用其他策略。如果需要,可以插入自定义策略。


我基本上也在找同样的东西。具体来说,我想要的是:

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

我最终所做的是创建自己的中间件函数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("/");
    }
  });
});

无论如何,这种方法主要被设计为灵活和简单。我相信有很多方法可以改善它。如果你有任何建议,我非常希望得到你的反馈。

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


有一个项目叫Drywall,实现了一个用户登录系统与护照,也有一个用户管理管理面板。如果你正在寻找一个功能齐全的用户认证和管理系统,类似于Django提供的Node.js,这就是它。我发现,对于构建一个需要用户身份验证和管理系统的节点应用程序来说,这是一个非常好的起点。有关护照如何工作的信息,请参阅Jared Hanson的回答。


几年过去了,我想介绍我的Express身份验证解决方案。它叫做Lockit。你可以在GitHub上找到这个项目,也可以在我的博客上找到一个简短的介绍。

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

easy to use: set up your DB, npm install, require('lockit'), lockit(app), done routes already built-in (/signup, /login, /forgot-password, etc.) views already built-in (based on Bootstrap but you can easily use your own views) it supports JSON communication for your AngularJS / Ember.js single page apps it does NOT support OAuth and OpenID. Only username and password. it works with several databases (CouchDB, MongoDB, SQL) out of the box it has tests (I couldn't find any tests for Drywall) it is actively maintained (compared to everyauth) email verification and forgot password process (send email with token, not supported by Passport) modularity: use only what you need flexibility: customize all the things

看一下这些例子。


使用mongo的简单示例,用于为ie 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);
}

下面是两个流行的用于node js身份验证的Github库:

https://github.com/jaredhanson/passport(易受暗示)

https://nodejsmodules.org/pkg/everyauth


对身份验证的另一种理解是passdless,这是express的一个基于令牌的身份验证模块,它绕过了密码[1]的固有问题。它实现起来很快,不需要太多表单,并且为普通用户提供了更好的安全性(完全披露:我是作者)。

[1]:密码过时


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

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

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


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

它会给你一个要求。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的作者。


slim-auth

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

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
    }
})

关于手摇方法,要注意一点:

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

与这里的一些建议相反,身份验证并不简单,处理解决方案也不总是简单的。我推荐passsportjs和bcrypt。

但是,如果你决定着手解决问题,可以看看express js提供的例子,以获得灵感。

祝你好运。