У меня есть бэкэнд и внешний интерфейс, работающие в разных доменах. Бэкэнд предлагает API для внешнего интерфейса, и я контролирую оба. Бэкэнд отправляет куки на внешний интерфейс, который я собираюсь отправить, и отправит обратно на дальнейшие запросы. Мне не нужен JavaScript для чтения куки, я просто хочу, чтобы он извлекался из бэкэнда и отправлялся обратно в бэкэнд .
Для целей этого эксперимента серверная часть работает на http://api.localtest.me:8000, а внешний интерфейс - на http://foo.lvh.me:3000. Оба имени хоста разрешаются до 127.0.0.1, так как я запускаю их на своем собственном компьютере разработчика в момент.
Сервер отправляет следующие заголовки CORS:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://foo.lvh.me:3000
, чтобы позволить клиенту общаться с сервером и получать куки. Полный запрос и ответ выглядят так:
curl.exe -H "Accept: application/json, text/plain, */*" -H "DNT: 1" -H "Origin: http://foo.lvh.me:3000" -H "Referer: http://foo.lvh.me:3000" -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" --verbose http://app.localtest.me:8000/api-v1/current-user/
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to app.localtest.me (127.0.0.1) port 8000 (#0)
> GET /api-v1/current-user/ HTTP/1.1
> Host: app.localtest.me:8000
> Accept: application/json, text/plain, */*
> DNT: 1
> Origin: http://foo.lvh.me:3000
> Referer: http://foo.lvh.me:3000
> User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
>
< HTTP/1.1 200 OK
< Date: Wed, 16 Jan 2019 09:45:37 GMT
< Server: WSGIServer/0.2 CPython/3.7.1
< Content-Type: application/json
< Vary: Accept, Cookie, Origin
< Allow: GET, HEAD, OPTIONS
< X-Frame-Options: SAMEORIGIN
< Content-Length: 2
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Origin: http://foo.lvh.me:3000
< Set-Cookie: sessionid=v04omdilmwz13h1vcirdz8r8pd6r5awv; expires=Wed, 30 Jan 2019 09:45:37 GMT; HttpOnly; Max-Age=1209600; Path=/; SameSite=Lax
<
{}* Connection #0 to host app.localtest.me left intact
Сервер очень четко отправляет Set-Cookie
. Теперь, когда я делаю то же самое в Chrome, запрос и ответы выглядят так:
Обратите внимание на отсутствие Set-Cookie
. Где это печенье съели? Я шаг за шагом отлаживал бэкэнд и увидел, что заголовок Set-Cookie записывается в поток, который идет в браузер.
Внешний интерфейс использует axios с учетными данными:
axios.get("http://api.localtest.me:8000/api-v1/current-user/", {withCredentials: true}).then(response => { console.log(response) })
Запрос, как в response.request
, кажется, показывает withCredentials
, правильно установленный:
Я заменил вызов axios на:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://api.localtest.me:8000/api-v1/current-user/', true);
xhr.withCredentials = true;
xhr.send(null);
и поведение точно такое же. Я изменяю withCredentials
на false
, и поведение остается таким же. Кажется, это идет вразрез с тем, что withCredentials
должен делать (выделено мной):
Свойство XMLHttpRequest.withCredentials является логическим значением, которое
указывает, должны ли межсайтовые запросы Access-Control быть
сделано с использованием учетных данных, таких как куки , заголовки авторизации или TLS
клиентские сертификаты. Настройка withCredentials не влияет на
запросы на тот же сайт.
В Firefox все немного по-другому. Он показывает cookie, отправленный с сервера, но не отправляет его обратно (на рисунке это не первый запрос, поэтому отправленный cookie должен быть таким же):
Я еще раз подтвердил, что это заголовки в запросе и ответе с помощью Fiddler:
Есть идеи, что здесь происходит?
Я попытался удалить SameSite: lax
из файла cookie, и поведение было таким же.
Для пояснения, ни один из них не был первым запросом, были предыдущие запросы, которые должны были установить cookie. Здесь вы можете увидеть три запроса один за другим: