使用从数据库加载凭据的节点基本身份验证

时间:2017-11-30 20:42:14

标签: javascript node.js authentication basic-authentication

我在服务器上使用NodeJ,我需要添加一些基本的auth功能,在任何文件(如html或js文件)下载到用户的浏览器之前,它会询问用户凭据。当我在没有登录时尝试查看源代码时,我无法像在this中那样访问它。 这适用于basic-auth模块,但仅适用于一个用户名和密码。

app.use(basicAuth('username', 'password'));

并且存在问题 - 我需要从数据库加载此credentions并检查用户是否为admin并比较他的加密密码。 我试过这个:

// ****
var basicAuth = require('basic-auth');
// ****
app.use(function (req, res, next) {

  var user = basicAuth(req);
  if (user) {
      var c = mysql.createConnection(db);
      var q = "SELECT * FROM user WHERE email = '" + user.name + "' AND admin = 1;";
      c.query(q, function (error, result) {
          c.end();
          if (error) {
              console.log(error);
              res.set('WWW-Authenticate', 'Basic realm="example"');
              return res.status(401).send();
          } else {
              if (result.length === 1 &&
                  bcrypt.compareSync(user.pass, result[0].password)) {

                  return next();
              } else {
                  res.set('WWW-Authenticate', 'Basic realm="example"');
                  return res.status(401).send();
              }
          }
      });
  } else {
      res.set('WWW-Authenticate', 'Basic realm="example"');
      return res.status(401).send();
  }
});

这样可以正常工作,但每次浏览器与服务器通信时都会调用此函数,这在加载简单页面时大约是70次(它不会再次要求用户提供凭据,&#34;只有&# 34;运行此功能)。此功能在每个请求上持续约200毫秒(因为查询和密码比较),总计加载持续 15秒不可接受。< /强>

有人可以帮助我,如何解决这个问题或推荐一些其他NodeJs库,这可以帮助我吗?

谢谢。

3 个答案:

答案 0 :(得分:0)

因此,如果您在数据库中存储哈希值并在数据库中存储管理员标志,那么为什么不执行以下选择请求:

var q = "SELECT * FROM user WHERE email = '" + user.name + "' AND password = '" + bcrypt.hashSync(user.pass) + "' AND admin = '1';";

如果只得到一个结果就可以,否则会被拒绝......

答案 1 :(得分:0)

如果我理解您的问题,则每次向服务器发出的请求都会运行您的身份验证,从而导致页面加载速度变慢。如果所有路线都在您正在进行的基本验证检查之后,我不明白某人如何登录,但这不是重点。

我设置节点应用程序的方法是为auth创建一系列函数,并将这些函数传递到我需要身份验证的特定路由,这样就可以为我可能正在服务的每个资源运行它。例如(psuedocode-ish):

// this is the authentication function array, each function in the array
// will be run when this array is called on a route, require next()
// after successful auth to continue on through the array/route handlers

// for your example, adminAuth would be the anonymous 
// function in your app.use method
var auth = [adminAuth];

// some random route examples

// this is an unprotected route
app.get('/login', loginHandler);

// these are protected routes and will have auth called
app.get('/api/users', auth, getUserHandler);
app.post('/api/user', auth, postUserHandler);

总而言之,您只能将auth调用放在受保护的资源上,这样就可以在不进行每次运行auth检查的情况下提供图像,html,js,css等。

答案 2 :(得分:0)

您的代码存在一些问题。我知道您需要对每个请求进行密码检查,但您不一定每次都需要访问数据库。假设你必须做HTTP Basic(不再推荐,我希望你是这样,你是通过HTTPS做的),那么你可以做的一个重大改进就是你创造了某种快速数据缓存。最简单的形式是内存(假设您的应用程序的每个实例保持自己的副本都可以),但您也可以根据您的使用情况使用memcached或redis等内容。

一个非常简单的实现就是保持一个内存中的JS对象,其密钥等于用户名,并且值等于从db返回的数据。类似的东西:

var userCache = {}; // something local to the module is the easiest way

然后,当您要查询数据库时,只需先检查userCache

if(userCache[user.name]) // check password
else
 // do your query and insert a successful result into the userCache for later. 

这样做可以显着减少每个资源的响应时间,但对于生产解决方案,您可能需要多层缓存(例如,内存缓存和redis或memcached缓存,以帮助跨多个实例的性能)你的应用)。

其次,您应该使用bcrypt检查的回调版本。在给定的请求上它不会更快,但它可能会为您的应用程序提供更多的整体请求容量,这应该会有所帮助。

第三,由于三个原因,您的实际SQL查询是个坏主意。首先,在大多数数据库中,使用SELECT *与选择所需的特定字段相比,性能会更差。这既是数据库端(查询缓存优化),也是节点端(例如,处理字符串响应和JS对象之间转换/转换的字段的时间)。其次,您正在进行的字符串连接是对SQL Injection attacks的邀请。请查看使用参数化查询。最后,如果您知道您的用户将是唯一的,那么您还应该在查询中添加LIMIT 1,因为您只需要一个结果,并且数据库可以再次对其进行一些优化。