1. Обзор
В этом коротком уроке мы узнаем, как устранить ошибку “Ответ на предполетный запрос имеет недопустимый код состояния HTTP 401”, которая может возникать в приложениях, поддерживающих связь между источниками и использующих Spring Security.
Во-первых, мы посмотрим, что такое запросы перекрестного происхождения, а затем исправим проблемный пример.
2. Запросы Перекрестного Происхождения
Короче говоря, запросы перекрестного происхождения-это HTTP-запросы, в которых источник и цель запроса различны. Это имеет место, например, когда веб-приложение обслуживается из одного домена, а браузер отправляет запрос AJAX на сервер в другом домене.
Для управления запросами между источниками сервер должен включить определенный механизм, известный как CORS, или совместное использование ресурсов между источниками.
Первым шагом в CORS является запрос OPTIONS , чтобы определить, поддерживает ли его цель запроса. Это называется запросом перед полетом.
Затем сервер может ответить на запрос перед полетом набором заголовков:
- Access-Control-Allow-Origin : Определяет, какие источники могут иметь доступ к ресурсу. “*”представляет любое происхождение
- Access-Control-Allow-Methods : Указывает разрешенные методы HTTP для запросов перекрестного происхождения
- Access-Control-Allow-Headers : Указывает разрешенные заголовки запросов для перекрестных запросов
- Access-Control-Max-Age : Определяет время истечения срока действия результата кэшированного предполетного запроса
Таким образом, если предполетный запрос не соответствует условиям, определенным из этих заголовков ответов, фактический последующий запрос вызовет ошибки, связанные с запросом перекрестного происхождения.
Это легко сделать добавьте поддержку CORS в наш сервис с пружинным питанием , но если он настроен неправильно, этот запрос перед полетом всегда завершится ошибкой с 401.
3. Создание REST API с поддержкой CORS
Чтобы смоделировать проблему, давайте сначала создадим простой REST API, который поддерживает запросы из разных источников:
@RestController @CrossOrigin("http://localhost:4200") public class ResourceController { @GetMapping("/user") public String user(Principal principal) { return principal.getName(); } }
Аннотация @CrossOrigin гарантирует, что наши API доступны только из источника, указанного в ее аргументе.
4. Защита Нашего API REST
Теперь давайте защитим наш REST API с помощью Spring Security:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .httpBasic(); } }
В этом классе конфигурации мы применили авторизацию ко всем входящим запросам. В результате он отклонит все запросы без действительного маркера авторизации.
5. Выполнение предполетного запроса
Теперь, когда мы создали наш REST API, давайте попробуем выполнить запрос перед полетом с помощью curl :
curl -v -H "Access-Control-Request-Method: GET" -H "Origin: http://localhost:4200" -X OPTIONS http://localhost:8080/user ... < HTTP/1.1 401 ... < WWW-Authenticate: Basic realm="Realm" ... < Vary: Origin < Vary: Access-Control-Request-Method < Vary: Access-Control-Request-Headers < Access-Control-Allow-Origin: http://localhost:4200 < Access-Control-Allow-Methods: POST < Access-Control-Allow-Credentials: true < Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH ...
Из вывода этой команды мы видим, что запрос был отклонен с 401.
Поскольку это команда curl , мы не увидим ошибку “Ответ на предполетный запрос имеет недопустимый код состояния HTTP 401” в выходных данных.
Но мы можем воспроизвести эту точную ошибку, создав интерфейсное приложение, которое использует наш REST API из другого домена, и запустив его в браузере.
6. Решение
Мы явно не исключили предполетные запросы из авторизации в нашей конфигурации безопасности Spring . Помните, что Spring Security по умолчанию защищает все конечные точки.
В результате наш API также ожидает маркер авторизации в запросе ОПЦИЙ.
Spring предоставляет готовое решение для исключения запросов параметров из проверок авторизации:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // ... http.cors(); } }
Метод cors() добавит предоставленный Spring CorsFilter в контекст приложения, который, в свою очередь, обходит проверки авторизации для запросов опций.
Теперь мы можем снова протестировать ваше приложение и убедиться, что оно работает.
7. Заключение
В этой короткой статье мы узнали, как исправить ошибку “Ответ на предполетный запрос имеет недопустимый код состояния HTTP 401”, которая связана с запросами Spring Security и cross-origin.
Обратите внимание, что в приведенном примере клиент и API должны работать в разных доменах или портах, чтобы воссоздать проблему. Например, мы можем сопоставить имя хоста по умолчанию клиенту и IP-адрес машины с нашим REST API при работе на локальной машине.
Как всегда, пример, показанный в этом учебнике, можно найти на Github .