Jak udostępniać pliki cookie z różnych źródeł? Dokładniej, jak używać Set-Cookie
nagłówka w połączeniu z nagłówkiem Access-Control-Allow-Origin
?
Oto wyjaśnienie mojej sytuacji:
Próbuję ustawić plik cookie dla interfejsu API, który działa localhost:4000
w aplikacji sieci Web, która jest hostowana localhost:3000
.
Wygląda na to, że otrzymuję odpowiednie nagłówki odpowiedzi w przeglądarce, ale niestety nie mają one znaczenia. Oto nagłówki odpowiedzi:
HTTP / 1.1 200 OK Access-Control-Allow-Origin: http: // localhost: 3000 Różne: pochodzenie, akceptacja-kodowanie Set-Cookie: token = 0d522ba17e130d6d19eb9c25b7ac58387b798639f81ffe75bd449afbc3cc715d6b038e426adeac3316f0511dc7fae3f7; Max-Age = 86400; Domena = localhost: 4000; Ścieżka = /; Wygasa = wt., 19 września 2017 21:11:36 GMT; HttpOnly Content-Type: application / json; charset = utf-8 Długość treści: 180 ETag: W / "b4-VNrmF4xNeHGeLrGehNZTQNwAaUQ" Data: pon., 18 września 2017 r., 21:11:36 czasu GMT Połączenie: utrzymuj przy życiu
Co więcej, widzę plik cookie pod, Response Cookies
gdy sprawdzam ruch za pomocą karty Sieć w narzędziach programistycznych Chrome. Jednak nie widzę, aby plik cookie został ustawiony na karcie Aplikacja poniżej Storage/Cookies
. Nie widzę żadnych błędów CORS, więc zakładam, że brakuje mi czegoś innego.
Jakieś sugestie?
Aktualizacja I:
Używam modułu żądania w aplikacji React-Redux do wysyłania żądania do /signin
punktu końcowego na serwerze. Do serwera używam express.
Serwer ekspresowy:
res.cookie ('token', 'xxx-xxx-xxx', {maxAge: 86400000, httpOnly: true, domain: 'localhost: 3000'})
Żądanie w przeglądarce:
request.post ({uri: '/ signin', json: {nazwa_użytkownika: 'userOne', hasło: '123456'}}, (err, response, body) => { // Robi coś })
Aktualizacja II:
Ustawiam teraz nagłówki żądań i odpowiedzi jak szalone, upewniając się, że są one obecne zarówno w żądaniu, jak i odpowiedzi. Poniżej zrzut ekranu. Zauważ, nagłówki Access-Control-Allow-Credentials
, Access-Control-Allow-Headers
, Access-Control-Allow-Methods
i Access-Control-Allow-Origin
. Patrząc na problem, który znalazłem na githubie Axiosa , mam wrażenie, że wszystkie wymagane nagłówki są już ustawione. Jednak nadal nie ma szczęścia ...
źródło
request
moduł nie jest przeznaczony do użytku w przeglądarce. Jak dotąd Axios wydaje się wykonywać świetną robotę. Otrzymuję teraz zarówno nagłówek:Access-Control-Allow-Credentials:true
iAccess-Control-Allow-Origin:http://localhost:3000
(używany do włączania CORS). Wydaje się to słuszne, aleSet-Cookie
nagłówek nic nie robi ...{ withCredentials: true }
jest to rzeczywiście wymagane przez stronę Axios, nagłówki serwerów również muszą być dokładnie sprawdzone (patrz stackoverflow.com/a/48231372/488666 )Odpowiedzi:
Co musisz zrobić
Aby umożliwić pomyślne odbieranie i wysyłanie plików cookie przez żądanie CORS, wykonaj następujące czynności.
Back-end (serwer): ustaw
Access-Control-Allow-Credentials
wartość nagłówka HTTP natrue
. Upewnij się również, że nagłówki HTTPAccess-Control-Allow-Origin
iAccess-Control-Allow-Headers
są ustawione, a nie z symbolem wieloznacznym*
.Aby uzyskać więcej informacji na temat ustawiania CORS w express js, przeczytaj dokumentację tutaj
Front-end (klient): ustaw
XMLHttpRequest.withCredentials
flagę natrue
, można to osiągnąć na różne sposoby w zależności od używanej biblioteki żądanie-odpowiedź:jQuery 1.5.1
xhrFields: {withCredentials: true}
Pobieranie ES6 ()
credentials: 'include'
axios :
withCredentials: true
Lub
Unikaj używania CORS w połączeniu z plikami cookie. Możesz to osiągnąć za pomocą proxy.
Jeśli z jakiegoś powodu nie unikaj tego. Rozwiązanie jest powyżej.
Okazało się, że Chrome nie ustawi ciasteczka, jeśli domena zawiera port. Ustawienie go na
localhost
(bez portu) nie stanowi problemu. Wielkie dzięki dla Erwina za tę wskazówkę!źródło
localhost
sprawdzenia tego tutaj: stackoverflow.com/a/1188145, a także może to pomóc w twojej sprawie ( stackoverflow.com/questions/50966861/… )Access-Control-Allow-Origin
w wyraźnej domenie, nie tylko,"*"
jest również wymagane. Wtedy byłaby to idealna odpowiedźUwaga dotycząca przeglądarki Chrome wydana w 2020 roku.
Jeśli więc Twój serwer zaplecza nie ustawi SameSite = None, Chrome użyje domyślnie SameSite = Lax i nie będzie używać tego pliku cookie z żądaniami {withCredentials: true}.
Więcej informacji https://www.chromium.org/updates/same-site .
Programiści Firefox i Edge również chcą udostępnić tę funkcję w przyszłości.
Specyfikację można znaleźć tutaj: https://tools.ietf.org/html/draft-west-cookie-incrementalism-01#page-8
źródło
Aby klient mógł odczytywać pliki cookie z żądań między źródłami, musisz mieć:
Wszystkie odpowiedzi z serwera muszą mieć w nagłówku:
Access-Control-Allow-Credentials: true
Klient musi wysłać wszystkie żądania z
withCredentials: true
opcjąW mojej implementacji z Angular 7 i Spring Boot osiągnąłem to dzięki następującym rozwiązaniom:
Po stronie serwera:
@CrossOrigin(origins = "http://my-cross-origin-url.com", allowCredentials = "true") @Controller @RequestMapping(path = "/something") public class SomethingController { ... }
origins = "http://my-cross-origin-url.com"
Część będzie dodaćAccess-Control-Allow-Origin: http://my-cross-origin-url.com
do nagłówka odpowiedzi każdego serweraallowCredentials = "true"
Część będzie dodaćAccess-Control-Allow-Credentials: true
do nagłówka odpowiedzi każdy serwer, który jest to, czego potrzebujemy, aby klient odczytywanie plików cookieStrona klienta:
import { HttpInterceptor, HttpXsrfTokenExtractor, HttpRequest, HttpHandler, HttpEvent } from "@angular/common/http"; import { Injectable } from "@angular/core"; import { Observable } from 'rxjs'; @Injectable() export class CustomHttpInterceptor implements HttpInterceptor { constructor(private tokenExtractor: HttpXsrfTokenExtractor) { } intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { // send request with credential options in order to be able to read cross-origin cookies req = req.clone({ withCredentials: true }); // return XSRF-TOKEN in each request's header (anti-CSRF security) const headerName = 'X-XSRF-TOKEN'; let token = this.tokenExtractor.getToken() as string; if (token !== null && !req.headers.has(headerName)) { req = req.clone({ headers: req.headers.set(headerName, token) }); } return next.handle(req); } }
Dzięki tej klasie faktycznie dodajesz dodatkowe elementy do wszystkich swoich żądań.
Pierwsza część
req = req.clone({ withCredentials: true });
dotyczy tego, czego potrzebujesz, aby wysłać każde zapytanie zwithCredentials: true
opcją. W praktyce oznacza to, że najpierw zostanie wysłane żądanie OPCJI, aby uzyskać między nimi pliki cookie i token autoryzacyjny, przed wysłaniem rzeczywistych żądań POST / PUT / DELETE, które wymagają dołączenia do nich tego tokena (w nagłówku), w zlecić serwerowi weryfikację i wykonanie żądania.Druga część to ta, która w szczególności obsługuje token anty-CSRF dla wszystkich żądań. W razie potrzeby odczytuje je z pliku cookie i zapisuje w nagłówku każdego żądania.
Pożądany wynik jest mniej więcej taki:
źródło
allowCredentials = "true"
w@CrossOrigin
adnotacji.W przypadku wersji ekspresowej zaktualizuj bibliotekę Express do
4.17.1
najnowszej stabilnej wersji. Następnie;W CorsOption: Zestaw
origin
do adresu URL lub adresu URL localhost produkcji frontend icredentials
natrue
przykładMoje pochodzenie ustawiam dynamicznie za pomocą modułu config npm .
Następnie w res.cookie:
Dla localhost: nie musisz w ogóle ustawiać tej samej witryny i bezpiecznej opcji, możesz ustawić
httpOnly
, abytrue
na http cookies do ataku Zapobieganie XSS i innych przydatnych opcji , w zależności od przypadku użycia.W środowisku produkcyjnym należy ustawić
sameSite
nanone
żądanie między źródłami isecure
natrue
. Pamiętaj, żesameSite
działa z ekspresową najnowszą wersją tylko teraz, a najnowsza wersja Chrome ustawia tylko pliki cookiehttps
, co wymaga bezpiecznej opcji.Oto jak uczyniłem mój dynamiczny
źródło
Odpowiedź Pima jest bardzo pomocna. W moim przypadku muszę użyć
Jeśli jest to data i godzina, nawet jeśli nie wygasła, nadal nie wyśle pliku cookie do zaplecza:
Mam nadzieję, że będzie to pomocne dla przyszłych osób, które mogą napotkać ten sam problem.
źródło