Вопрос или проблема
В сети есть несколько вопросов на ту же тему, но ничего не сработало.
У меня есть серверXYZ, на котором работает приложение Angular, веб-приложение tomcat для аутентификации и сервер nginx. Приложение Angular на порту 4200, приложение tomcat на 8080. Все на одном хосте.
У приложения Angular есть файл environment.ts
(закомментированная строка — один из моих тестов, смотрите конфигурацию nginx ниже):
export const environment = {
production: false,
// apiUrl: 'http://serverXYZ:444/api'
apiUrl: 'http://localhost:8080/backend'
};
Конфигурация nginx:
server {
listen 444;
server_name serverXYZ;
location / {
proxy_pass http://localhost:4200;
//websocket
proxy_set_header HOST $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass_request_headers on;
proxy_http_version 1.0;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_read_timeout 120s;
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}
}
location /api {
proxy_pass http://localhost:8080/backend;
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}
}
}
У веб-приложения tomcat есть следующее в web.xml:
...
<!-- Встроенная реализация CORS в Tomcat -->
<!-- https://tomcat.apache.org/tomcat-7.0-doc/config/filter.html -->
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
<init-param>
<param-name>cors.allowed.headers</param-name>
<param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization</param-value>
</init-param>
<init-param>
<param-name>cors.allowed.methods</param-name>
<param-value>GET,POST,HEAD,OPTIONS,PUT,DELETE</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Конец встроенной реализации CORS -->
...
Я использую свой компьютер, открываю браузер. С описанной конфигурацией, если я подключаюсь к http://serverXYZ:444, приложение отображается, но я получаю ошибку CORS при аутентификации (Ошибка CORS). Если вместо этого я использую конфигурацию с закомментированной строкой в environment.ts, аутентификация в браузере не говорит CORS, только 403. Конечно, API аутентификации протестирован и работает.
Я в недоумении, что не так? Также, решение этой проблемы с помощью nginx является моим предпочтительным вариантом, но не обязательно.
Правка:
Я добавляю еще одну информацию, я запускаю приложение angular с помощью “ng serve –disableHostCheck=true”. Не знаю, может быть, в этом случае следует использовать опцию “–publicHost” или “–host 0.0.0.0”?
Правка 2:
Еще одна вещь, которую я только что заметил, Firefox не выдает мне никаких ошибок для запроса OPTIONS, только Cors для POST.
Вы уже видели знаменитую статью “If is Evil”? В статье говорится, что
Единственные 100% безопасные вещи, которые можно сделать внутри if в контексте location, это:
return ...; rewrite ... last;
В большинстве случаев эти разъяснения слишком строгие, обычно вы можете безопасно использовать любую директиву nginx из ngx_http_rewrite_module
внутри блока if
. Однако использование любой другой директивы, включая add_header
, действительно небезопасно и может привести к непредсказуемым результатам. Вот как я бы написал такую конфигурацию:
map $request_method $route {
GET main;
POST main;
OPTIONS options;
default invalid;
}
map $request_method $api {
GET api;
POST api;
OPTIONS options;
default invalid;
}
server {
listen 444;
server_name serverXYZ;
location / {
try_files "" @$route;
}
location /api {
try_files "" @$api;
}
location @main {
# настройка подключения websocket
proxy_set_header HOST $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass_request_headers on;
proxy_http_version 1.0;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_read_timeout 120s;
# передача запроса
proxy_pass http://localhost:4200;
# добавление заголовков CORS к ответу
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}
location @api {
# преобразование URI
rewrite ^/api(.*) /backend$1 break;
# передача запроса
proxy_pass http://localhost:8080;
# добавление заголовков CORS к ответу
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}
location @options {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
location @invalid {
# метод не разрешен
add_header Allow "GET, POST, OPTIONS";
return 405;
}
}
Ответ или решение
Настройка CORS в Angular с использованием Nginx
CORS (Cross-Origin Resource Sharing) – это механизм безопасности веб-браузеров, который позволяет или запрещает веб-страницам делать запросы к доменам, отличным от того, с которого они были загружены. Настройка CORS имеет решающее значение для корректной работы приложений, которые взаимодействуют с серверными API. В данном случае мы столкнулись с проблемами настройки CORS для Angular-приложения, работающего на Nginx в связке с сервером Tomcat.
Структура системы
У вас есть следующая архитектура:
- Angular-приложение запущенное на
http://localhost:4200
. - Сервер Tomcat с вашим API на
http://localhost:8080/backend
. - Сервер Nginx, обрабатывающий запросы и работающий на порту
444
.
Проблема с CORS
Вы столкнулись с проблемами CORS, которые возникают из-за конфликтов между настройками Nginx и Tomcat. Основные ошибки:
- Failed CORS request: возникает при отправке запросов к API из вашего Angular-приложения.
- 403 Forbidden: означает, что запрос был отклонён на уровне сервера, скорее всего, из-за недостаточных прав при доступе к API.
Настройка Nginx для CORS
Рекомендуется использовать стандартные ключи управления CORS непосредственно в конфигурации Nginx. Ниже приводится структура более безопасной и упрощённой конфигурации Nginx без использования директив в условиях if
, которые могут привести к непредсказуемому поведению.
map $request_method $cors {
DEFAULT "";
GET "GET";
POST "POST";
OPTIONS "OPTIONS";
}
server {
listen 444;
server_name serverXYZ;
location / {
proxy_pass http://localhost:4200;
include proxy_params;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length, Content-Range';
if ($cors = "OPTIONS") {
add_header 'Content-Length' 0;
return 204;
}
}
location /api {
proxy_pass http://localhost:8080/backend;
include proxy_params;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length, Content-Range';
if ($cors = "OPTIONS") {
add_header 'Content-Length' 0;
return 204;
}
}
}
Настройка Tomcat для обработки CORS
Убедитесь, что ваш сервер Tomcat также корректно обрабатывает запросы CORS. Ваша конфигурация в web.xml
выглядит корректно, но убедитесь, что:
- Параметры корректны и включают все необходимые заголовки и методы.
- Фильтр CORS загружается для всех нужных URL-адресов.
Запуск Angular приложения
Обратите внимание на использование параметров при старте вашего Angular приложения:
- Параметр
--disableHostCheck=true
полезен для разработки, но в продакшене лучше уточнить хост с помощью--host 0.0.0.0
. Таким образом, ваше приложение будет доступно с других адресов, а не только с локального.
Отладка
- Откройте инструменты разработчика в вашем браузере и следите за вкладкой "Сеть" для анализа заголовков запросов.
- Проверьте консоль на наличие ошибок CORS и убедитесь, что определенные заголовки действительно отправляются.
- Тестируйте запросы с помощью инструментов вроде Postman, чтобы исключить проблемы на стороне клиента.
Заключение
Настройка CORS может оказаться сложной задачей, особенно в окружении с множеством компонентов. Правильная конфигурация сервера Nginx, а также серверного приложения Tomcat обеспечит вашему Angular-приложению необходимую безопасность и функциональность для успешного взаимодействия с API. Надеемся, что предоставленная информация будет полезной для решения ваших проблем с CORS.