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.