使用Node和Express 4进行基本HTTP身份验证

Dov*_*Dov 93 http http-authentication node.js express

看起来使用Express v3实现基本的HTTP身份验证是微不足道的:

app.use(express.basicAuth('username', 'password'));
Run Code Online (Sandbox Code Playgroud)

版本4(我使用4.2)删除了basicAuth中间件,所以我有点卡住了.我有以下代码,但它不会导致浏览器提示用户提供凭据,这是我想要的(以及我想象的旧方法):

app.use(function(req, res, next) {
    var user = auth(req);

    if (user === undefined || user['name'] !== 'username' || user['pass'] !== 'password') {
        res.writeHead(401, 'Access invalid for user', {'Content-Type' : 'text/plain'});
        res.end('Invalid credentials');
    } else {
        next();
    }
});
Run Code Online (Sandbox Code Playgroud)

Qwe*_*rty 82

使用vanilla JavaScript的简单基本身份验证(ES6)

app.use((req, res, next) => {

  // -----------------------------------------------------------------------
  // authentication middleware

  const auth = {login: 'yourlogin', password: 'yourpassword'} // change this

  // parse login and password from headers
  const b64auth = (req.headers.authorization || '').split(' ')[1] || ''
  const [login, password] = new Buffer(b64auth, 'base64').toString().split(':')

  // Verify login and password are set and correct
  if (login && password && login === auth.login && password === auth.password) {
    // Access granted...
    return next()
  }

  // Access denied...
  res.set('WWW-Authenticate', 'Basic realm="401"') // change this
  res.status(401).send('Authentication required.') // custom message

  // -----------------------------------------------------------------------

})
Run Code Online (Sandbox Code Playgroud)

为什么?

  • next()包含值" req.headers.authorization",但它也可以是空的,我们不希望它失败,因此奇怪的组合Basic <base64 string>
  • 节点不知道|| ''atob(),因此,btoa()

ES6 - > ES5

Buffer只是const...那种
var只是(x, y) => {...}
function(x, y) {...},只不过是两个const [login, password] = ...split()在一个任务

灵感来源(使用包)


以上是一个非常简单的示例,旨在超短并可快速部署到您的游乐场服务器.但正如评论中指出的那样,密码也可以包含冒号字符var.要从b64auth中正确提取它,您可以使用它.

  // parse login and password from headers
  const b64auth = (req.headers.authorization || '').split(' ')[1] || ''
  const strauth = new Buffer(b64auth, 'base64').toString()
  const splitIndex = strauth.indexOf(':')
  const login = strauth.substring(0, splitIndex)
  const password = strauth.substring(splitIndex + 1)

  // using shorter regex by @adabru
  // const [_, login, password] = strauth.match(/(.*?):(.*)/) || []
Run Code Online (Sandbox Code Playgroud)

另一方面,如果您只使用一次或几次登录,这是您需要的最低限度:(
您无需解析凭据)

function (req, res) {
//btoa('yourlogin:yourpassword') -> "eW91cmxvZ2luOnlvdXJwYXNzd29yZA=="
//btoa('otherlogin:otherpassword') -> "b3RoZXJsb2dpbjpvdGhlcnBhc3N3b3Jk"

  // Verify credentials
  if (  req.headers.authorization !== 'Basic eW91cmxvZ2luOnlvdXJwYXNzd29yZA=='
     && req.headers.authorization !== 'Basic b3RoZXJsb2dpbjpvdGhlcnBhc3N3b3Jk')        
    return res.status(401).send('Authentication required.') // Access denied.   

  // Access granted...
  res.send('hello world')
  // or call next() if you use it as middleware (as snippet #1)
}
Run Code Online (Sandbox Code Playgroud)

顺便说一句,您是否需要同时拥有安全和"公共"路径?考虑:改用.

var securedRoutes = require('express').Router()

securedRoutes.use(/* auth-middleware from above */)
securedRoutes.get('path1', /* ... */) 

app.use('/secure', securedRoutes)
app.get('public', /* ... */)

// example.com/public       // no-auth
// example.com/secure/path1 // requires auth
Run Code Online (Sandbox Code Playgroud)

  • 使用 `!==` 比较密码会使您容易受到计时攻击。https://en.wikipedia.org/wiki/Timing_attack 确保您使用恒定时间字符串比较。 (3认同)
  • 最好的... :) (2认同)
  • 不要使用`.split(':')`因为它会阻塞包含至少一个冒号的密码.根据[RFC 2617](http://www.ietf.org/rfc/rfc2617.txt),此类密码有效. (2认同)
  • 少依赖总是一件好事.我喜欢. (2认同)
  • 您还可以使用 RegExp `const [_, 登录名, 密码] = strauth.match(/(.*?):(.*)/) || []` 为冒号部分。 (2认同)
  • 由于定时攻击漏洞而被否决。这应该使用 https://nodejs.org/api/crypto.html#crypto_crypto_timingsafeequal_a_b 代替。例如,请参阅express-basic-auth如何实现这一点,https://github.com/LionC/express-basic-auth/blob/fa600c90707a69f1f3a0fa9f0295f412fc207e4e/index.js#L5-L17。 (2认同)
  • @Qwerty 我没有关于该主题的好文章(维基百科有帮助吗?请参阅 @hraban 共享的 https://en.wikipedia.org/wiki/Timing_attack)。你的观点是正确的——网络因素使得定时攻击不太实用。但依赖于此将是一种“通过模糊实现安全性”的情况——它可能会有所帮助,但它的保护效果不如实施恒定时间比较。请参阅 https://nvd.nist.gov/vuln/detail/CVE-2015-7576 了解 Rails 中的类似漏洞及其严重性评级。 (2认同)

小智 57

许多中间件在v4中被拉出Express核心,并被放入单独的模块中.基本的auth模块在这里:https://github.com/expressjs/basic-auth-connect

您的示例只需要更改为:

var basicAuth = require('basic-auth-connect');
app.use(basicAuth('username', 'password'));
Run Code Online (Sandbox Code Playgroud)

  • 该模块声称已被弃用(尽管它建议的替代方案似乎不满意) (18认同)
  • ^^绝对不满意,如密集无证.作为中间件使用的零示例,它可能是好的,但调用不可用.他们提供的示例非常适合一般性,但不适用于使用信息. (3认同)

rsp*_*rsp 52

TL; DR:

express.basicAuth已经消失
basic-auth-connect已被弃用
basic-auth没有任何逻辑
http-auth是一种矫枉过正
express-basic-auth是你想要的

更多信息:

既然您正在使用Express,那么您可以使用express-basic-auth中间件.

查看文档:

例:

const app = require('express')();
const basicAuth = require('express-basic-auth');

app.use(basicAuth({
    users: { admin: 'supersecret123' },
    challenge: true // <--- needed to actually show the login dialog!
}));
Run Code Online (Sandbox Code Playgroud)

  • 花了一些时间来弄清楚`challenge:true`选项 (12认同)
  • @rsp您知道如何仅将其应用于特定路线吗? (3认同)

Dov*_*Dov 33

我使用原始代码basicAuth来找到答案:

app.use(function(req, res, next) {
    var user = auth(req);

    if (user === undefined || user['name'] !== 'username' || user['pass'] !== 'password') {
        res.statusCode = 401;
        res.setHeader('WWW-Authenticate', 'Basic realm="MyRealmName"');
        res.end('Unauthorized');
    } else {
        next();
    }
});
Run Code Online (Sandbox Code Playgroud)

  • 这个模块被认为已弃用,请改用[jshttp/basic-auth](https://github.com/jshttp/basic-auth)(同样的api所以答案仍然适用) (9认同)

小智 32

我在express 4.0中用http-auth更改了基本认证,代码是:

var auth = require('http-auth');

var basic = auth.basic({
        realm: "Web."
    }, function (username, password, callback) { // Custom authentication method.
        callback(username === "userName" && password === "password");
    }
);

app.get('/the_url', auth.connect(basic), routes.theRoute);
Run Code Online (Sandbox Code Playgroud)


Mic*_*ael 19

似乎有多个模块可以做到这一点,有些模块已被弃用.

这个看起来很活跃:https:
//github.com/jshttp/basic-auth

这是一个使用示例:

// auth.js

var auth = require('basic-auth');

var admins = {
  'art@vandelay-ind.org': { password: 'pa$$w0rd!' },
};


module.exports = function(req, res, next) {

  var user = auth(req);
  if (!user || !admins[user.name] || admins[user.name].password !== user.pass) {
    res.set('WWW-Authenticate', 'Basic realm="example"');
    return res.status(401).send();
  }
  return next();
};




// app.js

var auth = require('./auth');
var express = require('express');

var app = express();

// ... some not authenticated middlewares

app.use(auth);

// ... some authenticated middlewares
Run Code Online (Sandbox Code Playgroud)

确保将auth中间件放在正确的位置,之前的任何中间件都不会被验证.


小智 7

function auth (req, res, next) {
  console.log(req.headers);
  var authHeader = req.headers.authorization;
  if (!authHeader) {
      var err = new Error('You are not authenticated!');
      res.setHeader('WWW-Authenticate', 'Basic');
      err.status = 401;
      next(err);
      return;
  }
  var auth = new Buffer.from(authHeader.split(' ')[1], 'base64').toString().split(':');
  var user = auth[0];
  var pass = auth[1];
  if (user == 'admin' && pass == 'password') {
      next(); // authorized
  } else {
      var err = new Error('You are not authenticated!');
      res.setHeader('WWW-Authenticate', 'Basic');      
      err.status = 401;
      next(err);
  }
}
app.use(auth);
Run Code Online (Sandbox Code Playgroud)


De *_*eur 7

安装express-basic-auth依赖项:

 npm i express-basic-auth
Run Code Online (Sandbox Code Playgroud)

需要您在创建应用程序时使用 auth 包

const app = require('express')();
const basicAuth = require('express-basic-auth');
Run Code Online (Sandbox Code Playgroud)

并像这样设置中间件:

app.use(basicAuth({
    users: { 'my-username': 'my-password' },
    challenge: true,
}));
Run Code Online (Sandbox Code Playgroud)


VIK*_*HLI 6

我们可以实现基本授权,而无需任何模块

//1.
var http = require('http');

//2.
var credentials = {
    userName: "vikas kohli",
    password: "vikas123"
};
var realm = 'Basic Authentication';

//3.
function authenticationStatus(resp) {
    resp.writeHead(401, { 'WWW-Authenticate': 'Basic realm="' + realm + '"' });
    resp.end('Authorization is needed');

};

//4.
var server = http.createServer(function (request, response) {
    var authentication, loginInfo;

    //5.
    if (!request.headers.authorization) {
        authenticationStatus (response);
        return;
    }

    //6.
    authentication = request.headers.authorization.replace(/^Basic/, '');

    //7.
    authentication = (new Buffer(authentication, 'base64')).toString('utf8');

    //8.
    loginInfo = authentication.split(':');

    //9.
    if (loginInfo[0] === credentials.userName && loginInfo[1] === credentials.password) {
        response.end('Great You are Authenticated...');
         // now you call url by commenting the above line and pass the next() function
    }else{

    authenticationStatus (response);

}

});
 server.listen(5050);
Run Code Online (Sandbox Code Playgroud)

资料来源:-http: //www.dotnetcurry.com/nodejs/1231/basic-authentication-using-nodejs