Node 및 Express 4를 사용한 기본 HTTP 인증


107

Express v3로 기본 HTTP 인증을 구현하는 것은 사소한 것 같습니다.

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

버전 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();
    }
});

2
뻔뻔한 플러그 : 저는이를 쉽게 만들고 필요한 대부분의 표준 기능을 갖춘 상당히 인기있는 모듈을 유지하고 있습니다. express-basic-auth
LionC

나는 회사의 프로젝트를위한 시간이 매우 짧은 기간에 (상황 인식 권한 부여 프로그램을 가능)을 적용했기 때문에 나는 최근 @LionC의 패키지를 포크 : npmjs.com/package/spresso-authy
castarco

답변:


108

바닐라 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] = 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 <base64 string>" 값을 포함 하지만 비어있을 수도 있으며 실패하지 않기를 원하므로|| ''
  • 노드는 알 수 없습니다 atob()btoa()따라서,Buffer

ES6-> ES5

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 = 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(/(.*?):(.*)/) || []

하나의 문에서 기본 인증

... 반면에 로그인을 한 번만 사용하거나 거의 사용 하지 않는 경우 필요한 최소한의 사항입니다. (인증 정보를 전혀 구문 분석 할 필요도 없습니다)

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

추신 : "보안"경로와 "공개"경로가 모두 필요합니까? express.router대신 사용 을 고려하십시오 .

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
제비 최고의 ... :)
Anupam BASAK

2
.split(':')적어도 하나의 콜론을 포함하는 암호를 막을 수 있으므로 사용하지 마십시오 . 이러한 암호는 RFC 2617 에 따라 유효합니다 .
Distortum

1
const [_, login, password] = strauth.match(/(.*?):(.*)/) || []콜론 부분에 RegExp 를 사용할 수도 있습니다 .
adabru

3
!==암호를 비교하는 데 사용하면 타이밍 공격에 취약 해집니다. en.wikipedia.org/wiki/Timing_attack 은 일정한 시간 문자열 비교를 사용하는지 확인하십시오.
hraban

1
사용 Buffer.from() // for strings또는 Buffer.alloc() // for numbers으로는 Buffer()보안 문제로 인해 사용되지 않습니다.
Mr. Alien

71

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

17
에 대해 알아낼 걸 렸어요 challenge: true옵션
Vitalii Zurian

1
@VitaliiZurian 좋은 점-답변에 추가했습니다. 지적 해 주셔서 감사합니다.
rsp

4
@rsp 특정 경로에만 이것을 적용하는 방법을 알고 있습니까?
Jorge L Hernandez

다른 종속성을 추가하지 않으려면 한 줄에 직접 기본 인증을 작성하는 것이 매우 쉽습니다.
Qwerty

클라이언트 URL은 어떻게 생겼습니까?
GGEv

57

많은 미들웨어가 v4의 Express 코어에서 분리되어 별도의 모듈에 배치되었습니다. 기본 인증 모듈은 다음과 같습니다. https://github.com/expressjs/basic-auth-connect

귀하의 예제는 다음과 같이 변경해야합니다.

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

19
(이 제안하는 대안은 만족스럽지 보이지만)이 모듈의 주장은 더 이상 사용되지 수
Arnout Engelen

3
^^ 조밀하게 문서화되지 않은 것처럼 절대적으로 불만족 스럽습니다. 미들웨어로 사용하는 예가 전혀 없는데, 아마 좋을 것 같지만 호출을 사용할 수 없습니다. 그들이 제공하는 예는 일반성에는 좋지만 사용 정보에는 적합하지 않습니다.
Wylie Kulik

예, 이건 더 이상 사용되지 않고 권장되는 문서는 적지 만
Loourr

1
내가 사용하는 방법을 설명한 basic-auth에서 라이브러리를 이 답변
Loourr

코드에 일반 텍스트로 암호를 넣는 것을 기반으로 전체 모듈이 어떻게 존재합니까 ? 적어도 base64에서 비교함으로써 그것을 모호하게 만드는 것은 약간 더 나은 것 같습니다.
user1944491

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

10
이 모듈은 더 이상 사용되지 않는 것으로 간주됩니다. 대신 jshttp / basic-auth를 사용합니다 (동일한 API이므로 답변이 계속 적용됨)
Michael

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

1
이것은 말 그대로 플러그 앤 플레이입니다. 우수합니다.
sidonaldson

20

이를 수행하는 여러 모듈이있는 것으로 보이며 일부는 더 이상 사용되지 않습니다.

이것은 활성화 된 것 같습니다 :
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

auth미들웨어를 올바른 위치에 배치 했는지 확인하십시오 . 그 이전의 미들웨어는 인증되지 않습니다.


나는 실제로 'basic-auth-connect'를 선호하지만 이름은 나쁘지만 기능면에서는 'basic-auth'보다 낫습니다. 후자는 권한 헤더를 구문 분석하는 것뿐입니다. 여전히 implement프로토콜을 직접
사용해야

완전한! 감사합니다. 이것은 효과가 있었고 모든 것을 멋지게 설명했습니다.
Tania Rascia

나는 이것을 시도했지만 연속 루프를 통해 로그인하도록 계속 요청합니다.
jdog

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

출처 :-http: //www.dotnetcurry.com/nodejs/1231/basic-authentication-using-nodejs


1

Express는이 기능을 제거했으며 이제 기본 인증 라이브러리 를 사용할 것을 권장합니다 .

다음은 사용 방법의 예입니다.

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)

이 경로로 요청을 보내려면 기본 인증 용으로 형식이 지정된 Authorization 헤더 를 포함해야합니다 .

먼저 curl 요청을 보내려면 base64를 가져와야합니다. 인코딩 name:pass또는이 경우 aladdin:opensesame동일YWxhZGRpbjpvcGVuc2VzYW1l

컬 요청은 다음과 같습니다.

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

0
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);

희망은 문제를 해결할 수 있지만 코드에 대한 설명을 추가하여 사용자가 실제로 원하는 것을 완벽하게 이해할 수 있도록하십시오.
Jaimil Patel
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.