Jamyy's Weblog

node.js 正向代理加入 basic auth 驗證機制

by Jamyy on 六月.06, 2016, under Linux

基於這篇文章的正向代理程式碼 (forward-proxy.js) 加以修改, 讓 proxy 使用者必須通過驗證才能連網。

安裝額外模組

$ npm i http-auth
$ npm i http-proxy

在 forward-proxy.js 所在路徑下產生 users.htpasswd 檔案

$ htpasswd -c users.htpasswd simon

New password: 輸入 simon 這個帳號的密碼
Re-Type new password: 再次輸入 simon 這個帳號的密碼
Adding password for user simon

編輯 forward-proxy.js, 加入 Basic Authentication 機制

$ vi forward-proxy.js

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

  var basic = auth.basic({
           realm: "Authentication is required.",
           file: __dirname + "/users.htpasswd"
      });
  var httpProxy = require('http-proxy');

  httpProxy.createServer(basic, { target: {host: 'localhost', port: 8083} }).listen(8081);

  var server = http.createServer( basic, httpUserRequest ).listen(8082);

  http.createServer( httpUserRequest ).listen(8083);

運行 forward-proxy.js

$ nodejs forward-proxy.js

調整 Firefox 瀏覽器 Proxy 設定



加入 basic auth 機制的完整 forward-proxy.js 程式碼

var http = require('http');
var net = require('net');
 
var debugging = 0;
 
var regex_hostport = /^([^:]+)(:([0-9]+))?$/;
 
function getHostPortFromString( hostString, defaultPort ) {
  var host = hostString;
  var port = defaultPort;
 
  var result = regex_hostport.exec( hostString );
  if ( result != null ) {
    host = result[1];
    if ( result[2] != null ) {
      port = result[3];
    }
  }
 
  return( [ host, port ] );
}
 
// handle a HTTP proxy request
function httpUserRequest( userRequest, userResponse ) {
  if ( debugging ) {
    console.log( '  > request: %s', userRequest.url );
  }
 
  var httpVersion = userRequest['httpVersion'];
  var hostport = getHostPortFromString( userRequest.headers['host'], 80 );
 
  // have to extract the path from the requested URL
  var path = userRequest.url;
  result = /^[a-zA-Z]+:\/\/[^\/]+(\/.*)?$/.exec( userRequest.url );
  if ( result ) {
    if ( result[1].length > 0 ) {
      path = result[1];
    } else {
      path = "/";
    }
  }
 
  var options = {
    'host': hostport[0],
    'port': hostport[1],
    'method': userRequest.method,
    'path': path,
    'agent': userRequest.agent,
    'auth': userRequest.auth,
    'headers': userRequest.headers
  };
 
  if ( debugging ) {
    console.log( '  > options: %s', JSON.stringify( options, null, 2 ) );
  }
 
  var proxyRequest = http.request(
    options,
    function ( proxyResponse ) {
      if ( debugging ) {
        console.log( '  > request headers: %s', JSON.stringify( options['headers'], null, 2 ) );
      }
 
      if ( debugging ) {
        console.log( '  < response %d headers: %s', proxyResponse.statusCode, JSON.stringify( proxyResponse.headers, null, 2 ) );
      }
 
      userResponse.writeHead(
        proxyResponse.statusCode,
        proxyResponse.headers
      );
 
      proxyResponse.on(
        'data',
        function (chunk) {
          if ( debugging ) {
            console.log( '  < chunk = %d bytes', chunk.length );
          }
          userResponse.write( chunk );
        }
      );
 
      proxyResponse.on(
        'end',
        function () {
          if ( debugging ) {
            console.log( '  < END' );
          }
          userResponse.end();
        }
      );
    }
  );
 
  proxyRequest.on(
    'error',
    function ( error ) {
      userResponse.writeHead( 500 );
      userResponse.write(
        "<h1>500 Error</h1>\r\n" +
        "<p>Error was <pre>" + error + "</pre></p>\r\n" +
        "</body></html>\r\n"
      );
      userResponse.end();
    }
  );
 
  userRequest.addListener(
    'data',
    function (chunk) {
      if ( debugging ) {
        console.log( '  > chunk = %d bytes', chunk.length );
      }
      proxyRequest.write( chunk );
    }
  );
 
  userRequest.addListener(
    'end',
    function () {
      proxyRequest.end();
    }
  );
}
 
function main() {
  var port = 8081; // default port if none on command line

  // check for any command line arguments
  for ( var argn = 2; argn < process.argv.length; argn++ ) {
    if ( process.argv[argn] === '-p' ) {
      port = parseInt( process.argv[argn + 1] );
      argn++;
      continue;
    }
 
    if ( process.argv[argn] === '-d' ) {
      debugging = 1;
      continue;
    }
  }
 
  if ( debugging ) {
    console.log( 'server listening on port ' + port );
  }
 
  // start HTTP server with custom request handler callback function
  var auth = require('http-auth');
  var basic = auth.basic({
           realm: "Authentication is required.",
           file: __dirname + "/users.htpasswd"
      });
  var httpProxy = require('http-proxy');
  httpProxy.createServer(basic, { target: {host: 'localhost', port: port+2} }).listen(port);
  var server = http.createServer( basic, httpUserRequest ).listen(port+1);
  http.createServer( httpUserRequest ).listen(port+2);

  // add handler for HTTPS (which issues a CONNECT to the proxy)
  server.addListener(
    'connect',
    function ( request, socketRequest, bodyhead ) {
      var url = request['url'];
      var httpVersion = request['httpVersion'];
 
      var hostport = getHostPortFromString( url, 443 );
 
      if ( debugging )
        console.log( '  = will connect to %s:%s', hostport[0], hostport[1] );
 
      // set up TCP connection
      var proxySocket = new net.Socket();
      proxySocket.connect(
        parseInt( hostport[1] ), hostport[0],
        function () {
          if ( debugging )
            console.log( '  < connected to %s/%s', hostport[0], hostport[1] );
 
          if ( debugging )
            console.log( '  > writing head of length %d', bodyhead.length );
 
          proxySocket.write( bodyhead );
 
          // tell the caller the connection was successfully established
          socketRequest.write( "HTTP/" + httpVersion + " 200 Connection established\r\n\r\n" );
        }
      );
 
      proxySocket.on(
        'data',
        function ( chunk ) {
          if ( debugging )
            console.log( '  < data length = %d', chunk.length );
 
          socketRequest.write( chunk );
        }
      );
 
      proxySocket.on(
        'end',
        function () {
          if ( debugging )
            console.log( '  < end' );
 
          socketRequest.end();
        }
      );
 
      socketRequest.on(
        'data',
        function ( chunk ) {
          if ( debugging )
            console.log( '  > data length = %d', chunk.length );
 
          proxySocket.write( chunk );
        }
      );
 
      socketRequest.on(
        'end',
        function () {
          if ( debugging )
            console.log( '  > end' );
 
          proxySocket.end();
        }
      );
 
      proxySocket.on(
        'error',
        function ( err ) {
          socketRequest.write( "HTTP/" + httpVersion + " 500 Connection error\r\n\r\n" );
          if ( debugging ) {
            console.log( '  < ERR: %s', err );
          }
          socketRequest.end();
        }
      );
 
      socketRequest.on(
        'error',
        function ( err ) {
          if ( debugging ) {
            console.log( '  > ERR: %s', err );
          }
          proxySocket.end();
        }
      );
    }
  ); // HTTPS connect listener
}
 
main();



Ref:



:,