可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
It looks like implementing basic HTTP authentication with Express v3 was trivial:
app.use(express.basicAuth('username', 'password'));
Version 4 (I'm using 4.2) removed the basicAuth
middleware, though, so I'm a little stuck. I have the following code, but it doesn't cause the browser to prompt the user for credentials, which is what I'd like (and what I imagine the old method did):
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();
}
});
回答1:
Simple Basic Auth with 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) {
res.set('WWW-Authenticate', 'Basic realm="401"') // change this
res.status(401).send('Authentication required.') // custom message
return
}
// -----------------------------------------------------------------------
// Access granted...
next()
})
Why?
req.headers.authorization
contains the value "Basic <base64 string>
", but it can also be empty and we don't want it to fail, hence the weird combo of || ''
- Node doesn't know
atob()
and btoa()
, hence the Buffer
ES6 -> ES5
const
is just var
.. sort of
(x, y) => {...}
is just function(x, y) {...}
const [login, password] = ...split()
is just two var
assignments in one
source of inspiration (uses packages)
The above is a
super simple example that was intended to be
super short and quickly deployable to your playground server. But as was pointed out in the comments, passwords can also contain colon characters
:
. To correctly extract it from the
b64auth, you can use this.
// 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(/(.*?):(.*)/) || []
On the other side, if you only ever use one or very few logins, this is the bare minimum you need:
(you don't need to parse the credentials)
//btoa('yourlogin:yourpassword') -> "eW91cmxvZ2luOnlvdXJwYXNzd29yZA=="
// Verify login and password is correct
if (req.headers.authorization !== 'Basic eW91cmxvZ2luOnlvdXJwYXNzd29yZA==') {
// ...send('Authentication required.')
return
}
BTW, do you need to have both secure and "public" paths? Consider using express.router
instead.
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
回答2:
A lot of the middleware was pulled out of the Express core in v4, and put into separate modules. The basic auth module is here: https://github.com/expressjs/basic-auth-connect
Your example would just need to change to this:
var basicAuth = require('basic-auth-connect');
app.use(basicAuth('username', 'password'));
回答3:
I used the code for the original basicAuth
to find the answer:
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();
}
});
回答4:
I changed in express 4.0 the basic authentication with http-auth, the code is:
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);
回答5:
TL;DR:
☒ express.basicAuth
is gone
☒ basic-auth-connect
is deprecated
☒ basic-auth
doesn't have any logic
☒ http-auth
is an overkill
☑ express-basic-auth
is what you want
More info:
Since you're using Express then you can use the express-basic-auth
middleware.
See the docs:
- https://www.npmjs.com/package/express-basic-auth
Example:
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!
}));
回答6:
There seems to be multiple modules to do that, some are deprecated.
This one looks active:
https://github.com/jshttp/basic-auth
Here's a use example:
// 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
Make sure you put the auth
middleware in the correct place, any middleware before that will not be authenticated.
回答7:
We can implement the basic authorization without needing any module
//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);
Source:- http://www.dotnetcurry.com/nodejs/1231/basic-authentication-using-nodejs
回答8:
Express has removed this functionality and now recommends you use the basic-auth library.
Here's an example of how to use:
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)
To send a request to this route you need to include an Authorization header formatted for basic auth.
Sending a curl request first you must take the base64 encoding of name:pass
or in this case aladdin:opensesame
which is equal to YWxhZGRpbjpvcGVuc2VzYW1l
Your curl request will then look like:
curl -H "Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l" http://localhost:3000/