- CORS Error Solutions
- Error: No ‘Access-Control-Allow-Origin’ header is present
- Error: Method xxx is not allowed
- Error: Request header field xxx is not allowed
- Error: The value of the ‘Access-Control-Allow-Origin’ header in the response must not be the wildcard ‘*’
- Error: The value of the ‘Access-Control-Allow-Credentials’ header in the response is ‘’ which must be ‘true’
- Настройка CORS в Spring Security
- Когда возникает проблема
- Включение CORS
- Что должно быть в http-ответе
- Настройка Spring Security
- Настройка CORS
- Проверка
- Настройка CORS в Spring Security: 10 комментариев
- Добавить комментарий Отменить ответ
CORS Error Solutions
When you send an HTTP request with a different domain than your page’s domain (or IP address + port number), a CORS error will occur. A CORS error means that the API server rejected your request. To access other domain API from your web page, the backend server you requested must set some CORS headers in the HTTP response to allow CORS requests. Below are some errors caused by incorrectly set HTTP response headers for CORS requests.
Error: No ‘Access-Control-Allow-Origin’ header is present
Error information in web browser console
Access to XMLHttpRequest at ‘http://localhost:8081/api’ from origin ‘http://localhost’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
GET http://localhost:8081/api net::ERR_FAILED 302
First, check that the URL, Method, and Content-Type you requested are correct.
Make sure the server API is up and running.
Enable CORS requests for your server API. Add Access-Control-Allow-Origin in HTTP response header.
Access-Control-Allow-Origin: *
or
Access-Control-Allow-Origin: http://your_page_domain
For example, in Java web projects.
response.setHeader(«Access-Control-Allow-Origin», «*»);
// or
response.setHeader(«Access-Control-Allow-Origin», «http://localhost»);
The API is not shared with other origins. You need to update the API CORS policy by set Access-Control-Allow-Origin in response headers.
Error: Method xxx is not allowed
Error information in web browser console
Access to XMLHttpRequest at ‘http://localhost:8081/api/delete’ from origin ‘http://localhost’ has been blocked by CORS policy: Method DELETE is not allowed by Access-Control-Allow-Methods in preflight response.
Add Access-Control-Allow-Methods: {method_name_in_error_message} in HTTP response header. Note that method names must be capitalized.
For example, in Java web projects.
response.setHeader(«Access-Control-Allow-Methods», «DELETE»);
The default allowed HTTP methods for CORS are GET, POST, and HEAD. For other HTTP methods like DELETE or PUT, you need to add it to HTTP response header Access-Control-Allow-Methods.
Error: Request header field xxx is not allowed
Error information in web browser console
Access to XMLHttpRequest at ‘http://localhost:8081/api/delete’ from origin ‘http://localhost’ has been blocked by CORS policy: Request header field my-header1 is not allowed by Access-Control-Allow-Headers in preflight response.
Access to XMLHttpRequest at ‘http://localhost:8081/api/get?name=Jack’ from origin ‘http://localhost’ has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.
Add Access-Control-Allow-Headers: {header_field_name_in_error_message} in HTTP response header.
For example, in Java web projects.
response.setHeader(«Access-Control-Allow-Headers», «my-header1»);
The default allowed HTTP headers for CORS requests are:
- Accept
- Accept-Language
- Content-Language
- Content-Type (value only be application/x-www-form-urlencoded, multipart/form-data, or text/plain)
- Range
For other HTTP headers, you need to add them to HTTP response header Access-Control-Allow-Headers.
Error: The value of the ‘Access-Control-Allow-Origin’ header in the response must not be the wildcard ‘*’
Error information in web browser console
Access to XMLHttpRequest at ‘http://localhost:8081/api/get’ from origin ‘http://localhost’ has been blocked by CORS policy: The value of the ‘Access-Control-Allow-Origin’ header in the response must not be the wildcard ‘*’ when the request’s credentials mode is ‘include’. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
Set the value of Access-Control-Allow-Origin to your page domain instead of * in HTTP response header. And set the value of Access-Control-Allow-Credentials to true.
For example, in Java web projects.
response.setHeader(«Access-Control-Allow-Origin», «http://localhost»);
response.setHeader(«Access-Control-Allow-Credentials», «true»);
When you send a CORS request with credentials, you must set a specific domain in Access-Control-Allow-Origin.
CORS request with credentials: withCredentials: true
const xhr = new XMLHttpRequest();
const url = ‘http://localhost:8081/api/get’;
xhr.open(‘GET’, url, true);
xhr.withCredentials = true;
xhr.onload = function ( ) {
if (xhr.readyState === xhr.DONE) {
if (xhr.status === 200) {
console.log(xhr.response);
}
}
};
xhr.send();
$.ajax({
url: «http://localhost:8081/api/get»,
method: «GET»,
xhrFields: {
withCredentials: true
},
}).done(function(res) {
console.log(res);
});
Error: The value of the ‘Access-Control-Allow-Credentials’ header in the response is ‘’ which must be ‘true’
Error information in web browser console
Access to XMLHttpRequest at ‘http://localhost:8081/api/get’ from origin ‘http://localhost’ has been blocked by CORS policy: The value of the ‘Access-Control-Allow-Credentials’ header in the response is » which must be ‘true’ when the request’s credentials mode is ‘include’. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
Add Access-Control-Allow-Credentials: true in HTTP response header.
For example, in Java web projects.
response.setHeader(«Access-Control-Allow-Credentials», «true»);
When the request’s credentials flag is true, the HTTP response header Access-Control-Allow-Credentials should be true.
Настройка CORS в Spring Security
В целях безопасности браузер запрещает JS-скрипту одного сайта обращаться на другой сайт без специального разрешения. Разрешение это реализуется с помощью технологии CORS (Cross-Origin Resource Sharing). Рассмотрим пример.
Когда возникает проблема
Создадим простое Spring Boot приложение с единственным Rest-контроллером:
@RestController public class HelloController < @GetMapping("/api/hello") public String hello()< return "Hello"; >>
Зависимость spring-boot-starter-security пока не будем добавлять в проект. Достаточно spring-boot-starter-web.
В браузере запрос http://localhost:8080/api/hello выполняется успешно:
Теперь запустим на другом порту localhost:4200 Angular-проект (статика и js), он будет выполнять ajax-запросы к нашему контроллеру.
Не обязательно проект должен быть на Angular, можно взять любой другой js-фреймворк или просто JQuery. Главное, мы имитируем обращение с другого сайта, то есть он должен быть на отдельном порту, не на 8080. Политика браузера такова, что другой порт для него считается такой же опасностью, как другой сайт.
Мы запустили Spring Boot приложение на localhost:8080, а Angular-клиент на порту localhost:4200.
Откроем в браузере localhost:4200 — откроется страница Angular-клиента, которая содержит JavaScript-код, выполняющий запрос http://localhost:8080/api/hello. В консоли мы увидим ошибку, что-то вроде:
Access to XMLHttpRequest at 'http://localhost:8080/api/hello' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Страница, естественно, не отображается. У новичка, не знакомого с CORS-политикой браузера, возникает искушение отключить весь CORS в Spring Security. Но у нас он еще не включен. Потому что дело не в Spring Security, а в браузере. И CORS придется не отключить, а наоборот, включить. И настроить.
Включение CORS
Еще раз вернемся к ошибке:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Тут говорится, что в http-ответе на запрос http://localhost:8080/api/hello отсутствует заголовок Access-Control-Allow-Origin.
Что должно быть в http-ответе
Итак, чтобы браузер не выдавал ошибку, надо чтобы Spring Boot приложение явно указывало в http-ответе в заголовке Access-Control-Allow-Origin домен(ы), с которого разрешены запросы. То есть, чтобы не отсекать нашего клиента, запущенного на http://localhost:4200, в ответе должен содержаться такой заголовок:
Access-Control-Allow-Origin: http://localhost:4200
Подразумевается, что Spring Boot приложение должно знать этот сайт http://localhost:4200 и давать ему разрешение на запросы с помощью вышеприведенного заголовка. Spring Boot приложение должно включать этот заголовок в ответ.
Если бы наш Angular-клиент находился на сайте myangularclient.com, то ему требовался бы соответственно такой заголовок:
Access-Control-Allow-Origin: myangularclient.com
А такой заголовок со звездочкой разрешит запросы всем сайтам:
Но последнее не безопасно.
Итак, займемся добавлением заголовка.
Настройка Spring Security
Для начала добавим Spring Security в проект:
org.springframework.boot spring-boot-starter-security
Как только это сделано, включится принудительная аутентификация всех запросов по умолчанию, и при вводе в браузере запроса мы будем перенаправлены на страницу логина:
Исправим это, разрешив все запросы для всех пользователей (наш фокус — CORS, а не аутентификация):
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter < @Override public void configure(HttpSecurity http) throws Exception < http.authorizeRequests().antMatchers("/**").permitAll(); >>
Теперь в браузере запрос http://localhost:8080/api/hello выполняется — возвращается hello, а вот при выполнении запроса с клиента возникает та же ошибка. Исправим ее.
Настройка CORS
Для этого реализуем еще метод addCorsMappings() интерфейса WebMvcConfigurer:
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer < @Override public void configure(HttpSecurity http) throws Exception < http.authorizeRequests().antMatchers("/**").permitAll(); >@Override public void addCorsMappings(CorsRegistry registry) < registry.addMapping("/**") .allowedOrigins("http://localhost:4200") .allowedMethods("*"); >>
В методе мы говорим, каким сайтам по каким url и какими методами можно делать запросы к нашему приложению, запущенному на 8080.
- только сайт http://localhost:4200 может делать запросы
- запрос можно делать абсолютно всеми методами (GET, POST, PUT и т.д.)
- обращаться к нашему приложению можно по любому внутреннему url — addMapping(«/**»)
Теперь проверим, как это работает с Angular-клиента.
Проверка
На картинке всплывающая подсказка говорит, что мы обращаемся по запросу
http://localhost:8080/api/hello
Запрос и ответ содержат такие заголовки (запрос смотрите внизу, а ответ вверху, важное выделено красным):
Запрос
Host: localhost:8080 Origin: http://localhost:4200
В запросе в заголовках Origin и Host содержится откуда и куда идет запрос. По ним приложение Spring Boot поймет, надо ли вообще включать в ответ заголовок Access-Control-Allow-Origin (если Origin и Host одинаковые, то не надо, проблемы в браузере в этом случае в принципе не возникнет).
Ответ
Далее наше Spring Boot приложение понимает, сайту http://localhost:4200 доступ разрешен (это мы настроили в методе addCorsMappings), так что в http-ответ оно включает такой заголовок:
Access-Control-Allow-Origin: http://localhost:4200
Браузер видит по этому заголовку, что сайту http://localhost:4200 доступ разрешен и не зажёвывает ответ с выбросом ошибки в консоль, как это делал раньше, а передает его клиенту. Все ок.
Исходный код Spring Boot примера (без Angular-клиента) доступен на GitHub.
Настройка CORS в Spring Security: 10 комментариев
Спасибо огромное за статью!
Перерыл кучу ресурсов, перепробовал много всего и потратил много часов — и только предложенное здесь решение оказалось полноценным решением проблемы.
Да, автор этого сайта просто снимает всю головную боль! 🙂
Вопрос к автору:
Если заголовок Access-Control-Allow-Origin: * обрабатывается браузером, то как например будет идти обработка запроса любым другим Rest клиентом (например вредоносным с подделкой заголовков)? Как тогда защититься? Так же могут за кршпулить api сайта (если не включать безопасность, jwt например).
Вопрос не правильно поставлен. Приложение не защищено, доступ к http://localhost:8080/api/hello можно получить из любого клиента, не надо даже подделывать никакие заголовки. А вот из браузера нельзя (если обращаться с другого домен c помощью JScript). И в статье речь о том, как снять защиту, выставляемую браузером, чтобы таки получить доступ с одного нужного домена http://localhost:4200.
Можно и так: @RestController
@CrossOrigin(origins=»http://localhost:4200″)
@RequestMapping(«api»)
public class HelloController < @GetMapping(«hello»)
public String hello() return «Hello»;
>
>
Спасибо автору! Действительно полезная информация.
К сожалению, я так и не смог победить решение с классом конфигурации, контекст просто не поднимался
2021-01-13 22:04:30.911 ERROR 5092 — [ restartedMain] o.s.boot.SpringApplication : Application run failed
nested exception is java.io.FileNotFoundException: class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.class] cannot be opened because it does not exist
упорно не находит класс, хотя пробовал уже разные танцы с бубном. В итоге решение с аннотацией @CrossOrigin(origins=”http://localhost:4200″)
над классом контроллера работает.
Полностью скопировал ваш файл SecurityConfig ошибка CORS ушла. Но теперь выдает ошибку 403
error: «Forbidden»
status: 403
Не пойму в чем проблема, почему в вас работает.
Добавить комментарий Отменить ответ
Прошу прощения: на комментарии временно не отвечаю.