关于node.js:Node和Express 4的基本HTTP身份验证

Basic HTTP authentication with Node and Express 4

使用Express v3实施基本的HTTP身份验证似乎很简单:

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

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

1
2
3
4
5
6
7
8
9
10
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();
    }
});


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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] = Buffer.from(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

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

})

注意:此"中间件"可以在任何处理程序中使用。只需删除next()并反转逻辑即可。请参阅下面的1语句示例,或此答案的编辑历史记录。

为什么?

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

ES6-> ES5

const只是var ..
(x, y) => {...}就是function(x, y) {...}
const [login, password] = ...split()只是两个var分配中的一项

灵感来源(使用软件包)

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

1
2
3
4
5
6
7
8
9
  // parse login and password from headers
  const b64auth = (req.headers.authorization || '').split(' ')[1] || ''
  const strauth = Buffer.from(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(/(.*?):(.*)/) || []

一条语句中的基本身份验证

...另一方面,如果您只使用一次或很少的登录,这是您所需要的最低要求:(您甚至根本不需要解析凭据)

1
2
3
4
5
6
7
8
9
10
11
12
13
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)
}

PS:您需要同时具有"安全"和"公共"路径吗?考虑改用express.router

1
2
3
4
5
6
7
8
9
10
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


TL; DR:

express.basicAuth不见了
basic-auth-connect已弃用
吗? basic-auth没有任何逻辑
吗? http-auth是个杀手<? express-basic-auth是您想要的

更多信息:

由于使用的是Express,因此可以使用express-basic-auth中间件。

参见文档:

  • https://www.npmjs.com/package/express-basic-auth

例:

1
2
3
4
5
6
7
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!
}));


许多中间件都从v4的Express核心中抽出,并放入单独的模块中。基本的身份验证模块在这里:https://github.com/expressjs/basic-auth-connect

您的示例只需更改为:

1
2
var basicAuth = require('basic-auth-connect');
app.use(basicAuth('username', 'password'));


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

1
2
3
4
5
6
7
8
9
10
11
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();
    }
});


我在express 4.0中使用http-auth更改了基本身份验证,代码为:

1
2
3
4
5
6
7
8
9
10
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);


似乎有多个模块可以执行此操作,其中一些已弃用。

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

这是一个使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// auth.js

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

var admins = {
  '[email protected]': { 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

确保将auth中间件放置在正确的位置,在此之前的任何中间件都不会进行身份验证。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//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);

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


Express已删除此功能,现在建议您使用basic-auth库。

以下是使用方法的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var http = require('http')
var auth = require('basic-auth')

// Create server
var server = http.createServer(function (req, res) {
  var credentials = auth(req)

  if (!credentials || credentials.name !== 'aladdin' || credentials.pass !== 'opensesame') {
    res.statusCode = 401
    res.setHeader('WWW-Authenticate', 'Basic realm="example"')
    res.end('Access denied')
  } else {
    res.end('Access granted')
  }
})

// Listen
server.listen(3000)

要将请求发送到此路由,您需要包括为基本身份验证设置格式的授权标头。

首先发送卷曲请求,您必须采用name:pass的base64编码,或者在这种情况下必须等于YWxhZGRpbjpvcGVuc2VzYW1laladdin:opensesame

您的curl请求将如下所示:

1
 curl -H"Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l" http://localhost:3000/