Вопрос или проблема
Я создал приложение на Angular и создал образ Docker, который позволяет ему работать на сервере Nginx (после его запуска). Для бэкенда у меня также есть реализованный в Docker проект. При попытке получить доступ к данным с бэкенда я сталкиваюсь с ошибкой, связанной с политикой CORS, в результате чего в браузере я вижу следующее: “…было заблокировано политикой CORS: отсутствует заголовок “Access-Control-Allow-Origin”…”
Чтобы решить эту проблему, я пробовал различные изменения конфигурации на сервере Nginx, например: (1) установить add_header “Access-Control-Allow-Origin” “http://0.0.0.0:8080”, (2) попробовать аналогичное изменение на стороне прокси, proxy_set_header “Access-Control-Allow-Origin” “http://0.0.0.0:8080” и т.д. Но ничего не сработало (обратите внимание, что “http://0.0.0.0:8080” указывает на бэкенд, а доступ к Angular осуществляется через “http://0.0.0.0:7000”).
Пример того, как выглядит мой файл конфигурации, представлен ниже:
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri /index.html = 404;
}
location /api {
proxy_pass http://0.0.0.0:8080;
proxy_set_header "Access-Control-Allow-Origin" "http://0.0.0.0:8080"
}
}
Может кто-нибудь из вас поделиться идеей, как решить эту проблему?
Спасибо!
Фактически блокировку выполняет веб-клиент (независимо от того, где он размещен в вашей настройке), поэтому вам необходимо разрешить исходный адрес, который клиент намеревается использовать, с внедренным Access-Control-Allow-Origin заголовком.
Этот заголовок и его значение должны быть известны клиенту до отправки запроса (который у вас не проходит), поэтому предыдущий ответ должен был предоставить его.
Как видно из ссылки, синтаксис заголовка не включает в себя номера портов. Насколько мне известно, 0.0.0.0 не является синтаксисом, который CORS интерпретирует так же, как сетевые службы, но я этого не тестировал и могу ошибаться.
Также, насколько я знаю, заголовок не указывает на протокол, а только на fqdn, например, www.example.com.
Я бы предположил, что конкретный IP-адрес может работать вместо fqdn, но не тестировал это и могу ошибаться.
Однако я использовал синтаксис подстановочного знака (*) указанный в ссылке, и это работает. Более специфический адрес предпочтителен, когда это возможно, но можно начать с подстановочного знака и посмотреть, работает ли внедрение заголовка вообще.
Так что попробуйте внедрить Access-Control-Allow-Origin: *
, это моя рекомендация.
Нет необходимости добавлять что-либо в конфигурацию nginx, такое как proxy_set_header. Вам нужно добавить фильтр CORS в ваше веб-приложение на Spring и разрешить запросы с разных источников.
Вот так:
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
final HttpServletResponse response = (HttpServletResponse) res;
log.info("request came for url {}", ((HttpServletRequest) req).getRequestURL());
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
response.setHeader("Access-Control-Max-Age", "3600");
if (HttpMethod.OPTIONS.name().equalsIgnoreCase(((HttpServletRequest) req).getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig config) throws ServletException {
}
}
Ответ или решение
Решение проблемы CORS в Dockerized Angular и Spring Boot приложениях
Введение
Проблема, с которой вы столкнулись, связана с политикой CORS (Cross-Origin Resource Sharing), которая запрещает веб-приложениям делать запросы к источникам, отличным от того, с которого было загружено само приложение. Это защищает пользователей от выполнения потенциально вредоносных запросов.
Основные шаги для решения проблемы
-
Настройка CORS на стороне Spring Boot:
Наиболее распространённым и правильным способом решения данной проблемы является добавление CORS-заголовков в вашем Spring Boot приложении. Вместо того, чтобы управлять CORS через Nginx, можно это сделать непосредственно в вашем Spring Boot коде. Вот пример кода, который поможет вам настроить нужные заголовки:import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; import org.springframework.core.annotation.Order; import org.springframework.http.HttpMethod; import java.io.IOException; @Component @Order(Ordered.HIGHEST_PRECEDENCE) public class CorsFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { final HttpServletResponse response = (HttpServletResponse) res; final HttpServletRequest request = (HttpServletRequest) req; response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type"); response.setHeader("Access-Control-Max-Age", "3600"); if (HttpMethod.OPTIONS.name().equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); } else { chain.doFilter(req, res); } } @Override public void init(FilterConfig filterConfig) {} @Override public void destroy() {} }
В этом коде мы устанавливаем заголовок
Access-Control-Allow-Origin
на ‘*’, что позволит всем доменам делать запросы к вашему серверу. Хотя это и удобно для разработки, желательно использовать конкретные домены (например,http://localhost:7000
), когда вы развертываете приложение в продакшен. -
Настройка Nginx (если всё же требуется):
Если вы хотите контролировать CORS через Nginx, измените вашу конфигурацию следующим образом:server { listen 80; location / { root /usr/share/nginx/html; index index.html index.htm; try_files $uri $uri /index.html = 404; } location /api { proxy_pass http://0.0.0.0:8080; 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 "Access-Control-Allow-Origin" "*"; proxy_set_header "Access-Control-Allow-Methods" "POST, GET, OPTIONS, DELETE, PUT"; proxy_set_header "Access-Control-Allow-Headers" "Authorization, Content-Type"; } }
Обратите внимание на то, что мы добавили заголовки CORS в секцию
location /api
. Это позволит вашему Nginx прокси не блокировать запросы из вашего Angular приложения. -
Тестирование:
После внесения всех необходимых изменений, перезапустите ваши контейнеры Docker и протестируйте, работает ли ваше приложение. Используйте инструменты разработчика в браузере для проверки того, что заголовки CORS корректно устанавливаются в ответах серверов.
Заключение
Настройка CORS – важный аспект разработки веб-приложений. Правильная конфигурация на стороне Spring Boot является ключом к решению проблемы блокировок CORS. Убедитесь, что вы тестируете ваш код в различных средах и проводите аудит безопасности перед тем, как выставлять настройки на продакшен.