Полное руководство по настройке прокси в Angular для вызовов API за корпоративным прокси или с проверкой подлинности Windows.

Разработчики Angular используют Angular CLI для локальной разработки. Одной из наиболее распространенных потребностей является настройка прокси-сервера в локальной среде разработки, чтобы избежать ошибки CORS, когда приложение Angular отправляет HTTP-запрос к API, находящемуся в другом домене. Angular CLI делает процесс относительно простым, но в некоторых крайних случаях может быть сложно.

В этой статье я собираюсь обсудить, как настроить прокси-сервер dev для

Настройте прокси для подключения к API на локальном хосте.

С помощью Angular CLI мы можем запустить локальный сервер разработки, используя приведенную ниже команду.

ng serve

Команда вызывает внутренний сервер разработки на основе сервера разработки webpack. По умолчанию сервер разработки работает на http://localhost:4200.

Когда приложению Angular необходимо вызвать свой серверный API, который также размещен локально по адресу http://localhost:3000, , мы столкнемся с ошибкой CORS, поскольку вызов HTTP использует другое происхождение (localhost:3000).

this.http.get('http://locahost:3000/api/')
    .subscribe(res => {...});

Проблема CORS может быть решена путем настройки прокси-сервера Angular dev. Пример конфигурации прокси можно создать ниже

// proxy.conf.json
{
  "/api": {
    "target": "http://localhost:3000",
    "secure": false,
  }
}
// we change the angular http call to remove the domain prefix
this.http.get('/api/')
    .subscribe(res => {...});

Как показано на следующей диаграмме, прокси-сервер находится между приложением Angular и бэкэнд-API и переводит вызовы «api/v1» в бэкэнд-API. Ошибка CORS не возникает, потому что вызов API теперь имеет тот же источник (localhost:4200/api).

Чтобы конфигурация прокси вступила в силу, ее необходимо передать в команду ng serve.

ng serve --proxy-config proxy.conf.json

Или мы можем добавить его в конфигурацию angular.json

"serve": {
  ...
  "options": {
    ...
    "proxyConfig": "proxy.conf.json"
  }
}

Подключиться к внешнему API за корпоративным прокси

Часто мы работали в корпоративной сети, и приложению Angular в локальной среде разработки также требовалось подключение к внешнему API. Используя предыдущий пример, нам может понадобиться вызвать http://abc.company.com/api в приложении Angular вместо вызова http://localhost:3000/api.

Для доступа к внешнему API за корпоративным прокси-сервером необходимо настроить переменные среды HTTP_PROXY и HTTPS_PROXY. Если прокси-сервер использует SSL-сертификат, флаг secure должен быть установлен в значение false, чтобы обойти проверку сертификата.

Для работы с корпоративным прокси нам нужно создать proxy.conf.js, как показано ниже.

const HttpsProxyAgent = require('https-proxy-agent');

const proxyConfig = [
  {
    context: '/api',
    pathRewrite: { '^/api': '' },
    target: 'https://api.abc.com',
    changeOrigin: true,
    secure: false
  }
];

function setupForCorporateProxy(proxyConfig) {
  const proxyServer = process.env.http_proxy || process.env.HTTP_PROXY;

  if (proxyServer) {
    const agent = new HttpsProxyAgent(proxyServer);
    proxyConfig.forEach(c => {
      c.agent = agent;
    });
  }
  return proxyConfig;
}

module.exports = setupForCorporateProxy(proxyConfig);

В приведенном выше примере мы проксируем запрос типа api/v1/client на внешний сервер https://api.abc.com/v1/client. Когда требуется корпоративный прокси, мы настраиваем в прокси объект HTTPS-агент на основе переменных окружения HTTP_PROXY и HTTPS_PROXY. Параметр secure:false добавлен для обработки пользовательского сертификата SSL в корпоративном прокси.

Чтобы использовать новую конфигурацию js для приложения Angular, выполните следующую команду.

ng serve --proxy-config proxy.conf.js

Стоит отметить, что существует два типа агентов: HttpsProxyAgent и HttpProxyAgent, необходимо выбрать подходящий исходя из настройки окружения.

Прокси к API с использованием проверки подлинности Windows (IIS)

Проверка подлинности Windows широко используется многими компаниями, полагающимися на экосистему Microsoft. Ситуация может быть сложной, если приложение Angular подключается к службе API, размещенной в IIS, которая защищена проверкой подлинности Windows.

Типичная проблема заключается в том, что вызов из приложения Angular к API возвращает 401 при использовании настройки прокси в локальной среде разработки.

Например, /api/v1/../login — это конечная точка, защищенная проверкой подлинности Windows, запрос к API от локально запущенного приложения Angular получает 401 неавторизованный ответ. Ниже приведен скриншот вкладки сети в инструментах разработчика Chrome.

Основная причина проблемы заключается в том, что проверка подлинности Windows основана на соединении, но прокси-сервер разрывает постоянное соединение.

Под капотом проверки подлинности Windows используется либо Kerberos, либо NTLM, любой из протоколов потребует постоянного подключения для сохранения состояния проверки подлинности.

Когда вызывается /api/v1/../login, браузер пытается установить соединение с сервером IIS через рукопожатие согласования NTLM, которое включает 3 части. Это сообщения типа 1, сообщения типа 2 и сообщения типа 3. Вы можете найти более подробную информацию о рукопожатии NTLM здесь. Вы уже могли заметить, что на приведенном выше снимке экрана показаны два HTTP-вызова для одного и того же запроса. Это первые две части рукопожатия.

Поскольку запрос передается локально, 3 сообщения рукопожатия были отправлены в 3 отдельных запросах (сокетах) через прокси-сервер, поэтому постоянное соединение не может быть сохранено в процессе. Вот почему последнее сообщение не появилось.

Чтобы решить эту проблему, нам нужно настроить прокси для поддержания единого соединения между браузером и сервером IIS. Пакетagentkeepalive может помочь нам в достижении этой цели. Обновленная конфигурация прокси показана ниже.

const Agent = require("agentkeepalive");

const keepaliveAgent = new Agent({
    maxSockets: 1,
    keepAlive: true,
    maxFreeSockets: 10,
    keepAliveMsecs: 1000,
    timeout: 60000,
    keepAliveTimeout: 30000 // free socket keepalive for 30 seconds
});

const PROXY_CONFIG = [
    {
        target: "http://localhost:3000",
        context: "/api",
        secure: false,
        changeOrigin: true,
        loglevel: "debug",
        agent: keepaliveAgent
    }
];
module.exports = PROXY_CONFIG;

В обновленной конфигурации прокси-сервера для maxSockets установлено значение 1, для флага keepAlive установлено значение true, а тайм-аут установлен на 30 секунд, что достаточно для завершения рукопожатия. Эта конфигурация направлена ​​на то, чтобы http.Agent поддерживал постоянное соединение между браузером и сервером IIS через прокси-сервер в процессе аутентификации.

Теперь запрос API /api/v1/../login должен работать.

Выше приведены журналы вкладки сети после успешной аутентификации с новой конфигурацией. Мы видим три запроса во время рукопожатия, и последний возвращает статус успеха HTTP 200.

Несколько схем в одном заголовке WWW-Authenticate

Другой возможной причиной ошибки проверки подлинности Windows является заголовок www-authenticate. В соответствии с RFC 7235 можно использовать несколько схем аутентификации в одном поле заголовка www-authenticate, хотя это может затруднить анализ поля.

Агенты должны будут проявлять особую осторожность при анализе значения поля заголовка WWW-
Authenticate или Proxy-Authenticate, если оно содержит
более одного запроса или если более одного поля заголовка WWW-Authenticate
предоставляется, поскольку само содержание запроса может
содержать список параметров аутентификации, разделенных запятыми.

Реальность такова, что поддержка браузера под вопросом. Ниже приведен пример заголовка www-authenticate с двумя схемами.

WWW-Authenticate: Negotiate, NTLM

Некоторые браузеры не смогут правильно проанализировать вышеуказанное, и это нарушит процесс рукопожатия NTLM. Чтобы решить эту проблему, мы можем использовать обратный вызов proxyRes в http-proxy-middleware, как показано ниже.

const onProxyRes = function (proxyRes, req, res) {
     var key = 'www-authenticate';
     proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(',');
};

// add it into the proxy config option
onProxyRes: onProxyRes

Полная конфигурация прокси выглядит следующим образом.

const Agent = require("agentkeepalive");

const keepaliveAgent = new Agent({
    maxSockets: 1,
    keepAlive: true,
    maxFreeSockets: 10,
    keepAliveMsecs: 1000,
    timeout: 60000,
    keepAliveTimeout: 30000 // free socket keepalive for 30 seconds
});
const onProxyRes = function (proxyRes, req, res) {
     var key = 'www-authenticate';
     proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(',');
};
const PROXY_CONFIG = [
    {
        target: "http://localhost:3000",
        context: "/api",
        secure: false,
        changeOrigin: true,
        onProxyRes: onProxyRes,
        agent: keepaliveAgent
    }
];
module.exports = PROXY_CONFIG;

С добавлением нового обратного вызова несколько схем в заголовке www-authenticate будут отправлены в нескольких строках, и рукопожатие согласования NTLM сможет продолжиться.

// From the original response header
www-authenticate: Negotiate, NTLM

// After the onProxRes callback function
www-authenticate: ['Negotiate', 'NTLM']

// It is equivalent to
< WWW-Authenticate: Negotiate
< WWW-Authenticate: NTLM

Краткое содержание

В этой статье мы обсудим, как избежать проблем с CORS, настроив прокси-сервер с помощью Angular CLI для локальной разработки. Я надеюсь, что это будет полезно для вас, если вы работаете с Angular за корпоративным прокси и/или используете IIS с аутентификацией Windows.

Удачного программирования!

Дополнительные материалы на PlainEnglish.io.

Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord .

Заинтересованы в масштабировании запуска вашего программного обеспечения? Ознакомьтесь с разделом Схема.