Chrome v37 / 38 CORS не работает (снова) с 401 для запросов перед полетом
Начиная с версии Chrome 37, запросы на междоменные запросы не выполняются (снова), если сервер имеет аутентификацию, даже если все заголовки CORS установлены правильно. Это на localhost
(мой dev ПК).
Некоторым из вас может быть известно об истории ошибок Chrome / CORS / auth, особенно при использовании HTTPS. Моя проблема не связана с HTTPS: у меня есть приложение AngularJS, обслуживаемое с localhost:8383
разговаривающее с сервером Java (Jetty) на localhost:8081
, активированное HTTP BASIC. GET работает нормально, но POST не работают с 401:
XMLHttpRequest cannot load http://localhost:8081/cellnostics/rest/patient. Invalid HTTP status code 401
Я ранее написал пользовательский (Java) фильтр CORS, который устанавливает правильные заголовки CORS, которые работали до v36. Он терпит неудачу в v37, а также последний v38 (38.0.2125.101 м). Он по- прежнему работает в Internet Explorer 11 (11.0.9600) и Opera 12.17 (assembly 1863).
- Повторно активировать расширения, не входящие в Chrome Web Store в Chrome v35 + (с повышенной безопасностью)
- Как я могу заставить мой браузер лгать о моей операционной системе?
- Как импортировать закладки Opera в Chrome?
- Уведомление HTML5 не работает в Mobile Chrome
- Включите строку состояния зависания ссылок в Google Chrome.
Запросы GET успешны, но POST не работают. Похоже, что Chrome предварительно отправляет все мои POST-сообщения из-за типа контента: «application / json» и что это запрошенный запрос OPTIONS, который не срабатывает.
В приложении «Угловое» я явно устанавливаю следующие заголовки запросов. AFAIK этот параметр для withCredentials
должен гарантировать, что учетные данные отправляются даже для запросов OPTIONS:
//Enable cross domain calls $httpProvider.defaults.useXDomain = true; //Send all requests, even OPTIONS, with credentials $httpProvider.defaults.withCredentials = true;
Ниже приведен запрос / ответ. Вы можете видеть, что метод OPTIONS включен в заголовке Access-Control-Allow-Methods
. Вы также можете увидеть, что источник приложения Javascript явно включен: Access-Control-Allow-Origin: http://localhost:8383
.
Remote Address:[::1]:8081 Request URL:http://localhost:8081/cellnostics/rest/medicaltest Request Method:OPTIONS Status Code:401 Full authentication is required to access this resource Request headers: Accept:*/* Accept-Encoding:gzip,deflate,sdch Accept-Language:en-US,en;q=0.8,af;q=0.6 Access-Control-Request-Headers:accept, content-type Access-Control-Request-Method:POST Connection:keep-alive Host:localhost:8081 Origin:http://localhost:8383 Referer:http://localhost:8383/celln-web/index.html User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.101 Safari/537.36 Response headers: Access-Control-Allow-Credentials:true Access-Control-Allow-Headers:Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With, Accept Access-Control-Allow-Methods:POST, GET, OPTIONS, PUT, DELETE Access-Control-Allow-Origin:http://localhost:8383 Access-Control-Max-Age:3600 Content-Length:0 Server:Jetty(8.1.8.v20121106) WWW-Authenticate:Basic realm="Cellnostics"
Кто-нибудь понял, что мне еще нужно делать? Я попытался очистить кеш Chrome перед тестированием, перезапуском и убедиться, что перед перезагрузкой не осталось фоновых процессов Chrome, поэтому я уверен, что не было никаких длительных проблем с кешем кэша.
Мне пришлось переключиться на IE 11 для тестирования моей веб-разработки. Тот факт, что одна и та же настройка клиента и сервера по-прежнему работает для IE и Opera, а также тот факт, что есть история ошибок Chrome / CORS, заставляет меня подозревать Chrome.
EDIT: Вот выдержка из списка событий net-internals в Chrome:
t=108514 [st=0] +URL_REQUEST_START_JOB [dt=4] --> load_flags = 336011264 (BYPASS_DATA_REDUCTION_PROXY | DO_NOT_SAVE_COOKIES | DO_NOT_SEND_AUTH_DATA | DO_NOT_SEND_COOKIES | MAYBE_USER_GESTURE | VERIFY_EV_CERT) --> method = "OPTIONS" --> priority = "LOW" --> url = "http://localhost:8081/cellnostics/rest/patient" ... t=108516 [st=2] HTTP_TRANSACTION_SEND_REQUEST_HEADERS --> OPTIONS /cellnostics/rest/patient HTTP/1.1 Host: localhost:8081 Connection: keep-alive Access-Control-Request-Method: POST Origin: http://localhost:8383 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.101 Safari/537.36 Access-Control-Request-Headers: accept, content-type Accept: */* Referer: http://localhost:8383/celln-web/index.html Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8,af;q=0.6
Таким образом, похоже, что заголовок авторизации не отправляется с предварительным withCredentials = true
OPTIONS, хотя я явно задал withCredentials = true
.
Однако почему IE и Opera все еще работают? Является ли Chrome более стандартизированным в этом отношении? Почему это сработало, а затем началось с отказа от версии v37?
EDIT: инструменты Chrome dev не показывают Content-Type
запроса в дампах выше, но здесь он из журнала Network. Первая фотография показывает POST, когда аутентификация сервера отключена, а тип контента правильно отправлен как «application / json». Второй рис., Когда включен auth, показывая, что запрос OPTIONS не работает (кажется, OPTIONS всегда отправляется с типом контента «text / plain»?).
- Каков самый быстрый способ открыть URL-адреса на новых вкладках через Selenium-Python?
- Как искать не ssl google?
- Есть ли способ отключить IPv6 в Google Chrome?
- Как открыть профиль Chrome через Python
- Есть ли плагин Google Bookmarks для Chrome?
- Отключить просмотр SSL в Chrome
- Как я могу сделать переадресацию кеширования Chrome?
- Chrome: Ctrl-K для поиска в Google и Ctrl-L для меня, я чувствую себя счастливым
@Cornel Masson, вы решили проблему? Я не понимаю, почему ваш сервер просит вас аутентифицировать запрос OPTIONS, но я столкнулся с этой же проблемой с сервером SAP NetWeaver. Я прочитал всю спецификацию CORS (я рекомендую), поэтому я могу разъяснить вам некоторые из ваших сомнений.
О вашем предложении
В приложении «Угловое» я явно устанавливаю следующие заголовки запросов. AFAIK этот параметр для withCredentials должен гарантировать, что учетные данные отправляются даже для запросов OPTIONS:
- В соответствии со спецификацией CORS, когда пользовательский агент (таким образом, браузер) предваряет запрос (запросы с помощью метода OPTIONS HTTP), он ДОЛЖЕН исключать учетные данные пользователя (cookies, HTTP-аутентификация …), поэтому любой запрос OPTIONS не может запрашиваться как аутентифицированный , Браузер запросит как аутентифицированный фактический запрос (тот, у кого запрошенный метод HTTP, такой как GET, POST …), но не запрос предполетной проверки.
- Поэтому браузеры НЕ ДОЛЖНЫ отправлять учетные данные в запрос OPTIONS. Они будут делать в реальных запросах. Если вы пишете withCredentials = true, браузер должен делать то, что я говорю.
Согласно вашему предложению:
Похоже, что Chrome все же отправляет все мои POST-сообщения из-за типа контента: «application / json»:
- В спецификации также говорится, что браузер запрашивает запрос перед полетом, когда заголовок не является «простым заголовком», и здесь у вас есть то, что это означает:
Заголовок называется простым заголовком, если имя поля заголовка является несоответствующим регистру ASCII совпадению для Accept, Accept-Language или Content-Language или если это несовместимое с ASCII совпадение для Content-Type и поле заголовка (исключая параметры) – это ASCII-регистр без учета регистра для приложения / x-www-form-urlencoded , multipart / form-data или text / plain .
Приложение / json не включено, поэтому браузер ДОЛЖЕН предварять запрос, как он. Если кто-нибудь найдет решение, это будет оценено.
EDIT: Я просто нашел человека с той же проблемой, которая отражает реальные проблемы, и если вы используете тот же сервер, что и он, вам повезет, https://evolpin.wordpress.com/2012/10/12/the-cors/
Тоже самое. Я использую аутентификацию Windows NTLM. До версии Chrome 37 работало нормально. В версиях 37, 38 он не работает с 401 (Несанкционированный) из-за отсутствия заголовка авторизации в предполетных ВАРИАНТАХ на обоих PUT и POST.
На стороне сервера находится Microsoft Web Api 2.1. Я пробовал различные CORS, включая последний пакет NuGet от Microsoft, но безрезультатно.
Мне нужно обходить Chrome, отправив GET-запрос вместо POST и разбив довольно огромные данные по нескольким запросам, так как URL имеет ограничение по размеру, естественно.
Вот заголовки запроса / ответа:
Request URL: http://localhost:8082/api/ConfigurationManagerFeed/ Method: OPTIONS Status: 401 Unauthorized Request Headers Accept: */* Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8,ru;q=0.6 Access-Control-Request-Headers: accept, content-type Access-Control-Request-Method: POST Connection: keep-alive Host: localhost:8082 Origin: http://localhost:8383 Referer: http://localhost:8383/Application/index.html User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.104 Safari/537.36 X-DevTools-Emulate-Network-Conditions-Client-Id: 49E7FC68-A65C-4318-9292-852946051F27 Response Headers Cache-Control: private Content-Length: 6388 Content-Type: text/html; charset=utf-8 Date: Fri, 24 Oct 2014 13:40:07 GMT Server: Microsoft-IIS/7.5 WWW-Authenticate: Negotiate NTLM X-Powered-By: ASP.NET
В MS IIS я реализовал еще один способ обхода путем переопределения жизненного цикла стандартной страницы Microsoft, то есть обработки ВАРИАНТОВ прямо в начале HTTP-запроса в global.ascx:
public class Global : HttpApplication { ///
,Check and cofigure CORS Pre-flight request on Begin Request lifecycle /// protected void Application_BeginRequest() { if (Request.Headers.AllKeys.Contains(CorsHandler.Origin) && Request.HttpMethod == "OPTIONS") { Response.StatusCode = (int)HttpStatusCode.OK; Response.Headers.Add(CorsHandler.AccessControlAllowCredentials, "true"); Response.Headers.Add(CorsHandler.AccessControlAllowOrigin, Request.Headers.GetValues(CorsHandler.Origin).First()); string accessControlRequestMethod = Request.Headers.GetValues(CorsHandler.AccessControlRequestMethod).FirstOrDefault(); if (accessControlRequestMethod != null) { Response.Headers.Add(CorsHandler.AccessControlAllowMethods, accessControlRequestMethod); } var hdrs = Request.Headers.GetValues(CorsHandler.AccessControlRequestHeaders).ToList(); hdrs.Add("X-Auth-Token"); string requestedHeaders = string.Join(", ", hdrs.ToArray()); Response.Headers.Add(CorsHandler.AccessControlAllowHeaders, requestedHeaders); Response.Headers.Add("Access-Control-Expose-Headers", "X-Auth-Token"); Response.Flush(); } } }public class Global : HttpApplication { ///
Check and cofigure CORS Pre-flight request on Begin Request lifecycle /// protected void Application_BeginRequest() { if (Request.Headers.AllKeys.Contains(CorsHandler.Origin) && Request.HttpMethod == "OPTIONS") { Response.StatusCode = (int)HttpStatusCode.OK; Response.Headers.Add(CorsHandler.AccessControlAllowCredentials, "true"); Response.Headers.Add(CorsHandler.AccessControlAllowOrigin, Request.Headers.GetValues(CorsHandler.Origin).First()); string accessControlRequestMethod = Request.Headers.GetValues(CorsHandler.AccessControlRequestMethod).FirstOrDefault(); if (accessControlRequestMethod != null) { Response.Headers.Add(CorsHandler.AccessControlAllowMethods, accessControlRequestMethod); } var hdrs = Request.Headers.GetValues(CorsHandler.AccessControlRequestHeaders).ToList(); hdrs.Add("X-Auth-Token"); string requestedHeaders = string.Join(", ", hdrs.ToArray()); Response.Headers.Add(CorsHandler.AccessControlAllowHeaders, requestedHeaders); Response.Headers.Add("Access-Control-Expose-Headers", "X-Auth-Token"); Response.Flush(); } } }
Мы столкнулись с этой же проблемой при попытке отладки внешнего приложения Angular 4, работающего на localhost: 4200 (с использованием Angular CLI Live Development Server). Угловое приложение делает HTTP-запросы к приложению ASP .Net WebApi2, запущенному на локальном хосте (веб-сервере IIS) с проверкой подлинности Windows. Ошибка возникла только при выполнении запроса POST в Chrome (хотя наш WebApi настроен для COR). Мы смогли временно решить проблему, запустив Fiddler и запустив обратный прокси до тех пор, пока мы не найдем этот пост (спасибо).
Сообщение об ошибке в отладчике Chrome: ответ на запрос предварительной проверки не проходит проверку контроля доступа: заголовок «Access-Control-Allow-Origin» отсутствует на запрошенном ресурсе
Добавление приведенного ниже кода в наш веб-файл Api2 – Global.asax разрешило эту проблему.
Если вы ищете «CorsHandler», вы можете просто заменить запись Джорджа строковыми строковыми значениями следующим образом:
protected void Application_BeginRequest() { if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS") { Response.StatusCode = (int)HttpStatusCode.OK; Response.Headers.Add("Access-Control-Allow-Credentials", "true"); Response.Headers.Add("Access-Control-Allow-Origin", Request.Headers.GetValues("Origin").First()); string accessControlRequestMethod = Request.Headers.GetValues("Access-Control-Request-Method").FirstOrDefault(); if (accessControlRequestMethod != null) { Response.Headers.Add("Access-Control-Allow-Methods", accessControlRequestMethod); } var hdrs = Request.Headers.GetValues("Access-Control-Request-Headers").ToList(); hdrs.Add("X-Auth-Token"); string requestedHeaders = string.Join(", ", hdrs.ToArray()); Response.Headers.Add("Access-Control-Allow-Headers", requestedHeaders); Response.Headers.Add("Access-Control-Expose-Headers", "X-Auth-Token"); Response.Flush(); } }
,protected void Application_BeginRequest() { if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS") { Response.StatusCode = (int)HttpStatusCode.OK; Response.Headers.Add("Access-Control-Allow-Credentials", "true"); Response.Headers.Add("Access-Control-Allow-Origin", Request.Headers.GetValues("Origin").First()); string accessControlRequestMethod = Request.Headers.GetValues("Access-Control-Request-Method").FirstOrDefault(); if (accessControlRequestMethod != null) { Response.Headers.Add("Access-Control-Allow-Methods", accessControlRequestMethod); } var hdrs = Request.Headers.GetValues("Access-Control-Request-Headers").ToList(); hdrs.Add("X-Auth-Token"); string requestedHeaders = string.Join(", ", hdrs.ToArray()); Response.Headers.Add("Access-Control-Allow-Headers", requestedHeaders); Response.Headers.Add("Access-Control-Expose-Headers", "X-Auth-Token"); Response.Flush(); } }
С уважением,
Крис