Dlaczego listonosz nie otrzymuje błędu „Brak żądanego źródła kontroli dostępu do źródła” w żądanym zasobie ”, gdy robi to mój kod JavaScript?

2500

Uwaga dotycząca modów : To pytanie dotyczy tego, dlaczego Postman nie podlega ograniczeniom CORS w taki sam sposób, jak XMLHttpRequest. To pytanie nie dotyczy sposobu naprawienia błędu „Brak kontroli dostępu - zezwolenia na pochodzenie”….

Przestań publikować :


Próbuję zrobić autoryzację za pomocą JavaScript , łącząc się z relaksującego API wbudowane w kolbie . Jednak po złożeniu żądania pojawia się następujący błąd:

XMLHttpRequest nie może załadować http: // myApiUrl / login . Żądany zasób nie zawiera nagłówka „Access-Control-Allow-Origin”. Dostęp „null” pochodzenia nie jest zatem dozwolony.

Wiem, że interfejs API lub zdalny zasób musi ustawić nagłówek, ale dlaczego zadziałało, gdy wysłałem żądanie za pośrednictwem rozszerzenia Postman do przeglądarki Chrome ?

To jest kod żądania:

$.ajax({
    type: "POST",
    dataType: 'text',
    url: api,
    username: 'user',
    password: 'pass',
    crossDomain : true,
    xhrFields: {
        withCredentials: true
    }
})
    .done(function( data ) {
        console.log("done");
    })
    .fail( function(xhr, textStatus, errorThrown) {
        alert(xhr.responseText);
        alert(textStatus);
    });
Pan Jedi
źródło
32
Czy wykonujesz żądanie z localhost lub bezpośrednio uruchamiasz HTML?
MD. Sahib Bin Mahboob,
@ MD.SahibBinMahboob Jeśli rozumiem twoje pytanie, przesyłam prośbę od hosta lokalnego - mam stronę na swoim komputerze i po prostu ją uruchamiam. Kiedy wdrażam witrynę na hoście, daje to ten sam wynik.
Pan Jedi,
6
bardzo podobne: stackoverflow.com/questions/10143093/…
cregox
8
Dla każdego, kto chce więcej czytać, MDN ma dobry artykuł na temat ajax i żądań krzyżowego pochodzenia: developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
Sam Eaton
1
Jedna ważna uwaga dla tego typu błędu w węźle js. MUSISZ ustawić nagłówki dostępu podczas uruchamiania pliku server.js PRZED rozpoczęciem konfigurowania tras. W przeciwnym razie wszystko będzie działało świetnie, ale pojawi się ten błąd, gdy będziesz wysyłać żądania z aplikacji.
Alex J

Odpowiedzi:

1343

Jeśli dobrze to zrozumiałem, wykonujesz XMLHttpRequest w innej domenie niż Twoja strona. Dlatego przeglądarka go blokuje, ponieważ zwykle ze względów bezpieczeństwa zezwala na żądanie tego samego źródła. Musisz zrobić coś innego, jeśli chcesz wykonać żądanie między domenami. Samouczek na temat tego, jak to osiągnąć, to Korzystanie z CORS .

Gdy korzystasz z listonosza, nie podlegają one tym zasadom. Cytat z Cross-Origin XMLHttpRequest :

Zwykłe strony internetowe mogą używać obiektu XMLHttpRequest do wysyłania i odbierania danych ze zdalnych serwerów, ale są one ograniczone tą samą zasadą pochodzenia. Rozszerzenia nie są tak ograniczone. Rozszerzenie może komunikować się ze zdalnymi serwerami spoza swojego źródła, pod warunkiem, że najpierw poprosi o uprawnienia krzyżowe.

MD Sahib Bin Mahboob
źródło
7
Masz rację. Wysyłam zapytanie do innej domeny niż moja strona. Interfejs API jest na serwerze i uruchamiam żądanie z hosta lokalnego. Zanim zaakceptuję odpowiedź, czy możesz mi wyjaśnić, co oznacza „bezpośrednie wykonanie żądania”? POSTMAN nie używa domeny?
Pan Jedi,
180
Przeglądarka nie blokuje żądania. Jedynymi przeglądarkami, które wprost blokują żądania pochodzące z różnych źródeł, jest IE7 lub starszy. Wszystkie przeglądarki, inne niż IE7 i starsze, implementują specyfikację CORS (IE8 i IE9 częściowo). Wszystko, co musisz zrobić, to wyrazić zgodę na żądania CORS na serwerze API, zwracając odpowiednie nagłówki na podstawie żądania. Powinieneś przeczytać o koncepcjach CORS na mzl.la/VOFrSz . Listonosz wysyła również zapytania za pośrednictwem XHR. Jeśli nie widzisz tego samego problemu podczas korzystania z listonosza, oznacza to, że nieświadomie nie wysyłasz tego samego żądania za pośrednictwem listonosza.
Ray Nicholus,
10
@ MD.SahibBinMahboob Listonosz NIE wysyła żądania kodu „z kodu Java / Python”. Wysyła żądanie bezpośrednio z przeglądarki. XHR w rozszerzeniach Chrome działa nieco inaczej, szczególnie gdy dotyczy to żądań pochodzących z różnych źródeł .
Ray Nicholus,
249

OSTRZEŻENIE: Używanie Access-Control-Allow-Origin: *może narazić interfejs API / witrynę na ataki typu CSRF. Przed użyciem tego kodu upewnij się, że rozumiesz ryzyko .

Rozwiązanie tego problemu jest bardzo proste, jeśli używasz PHP . Wystarczy dodać następujący skrypt na początku strony PHP, która obsługuje żądanie:

<?php header('Access-Control-Allow-Origin: *'); ?>

Jeśli używasz Node-red , musisz zezwolić na CORS w node-red/settings.jspliku , odznaczając następujące linie:

// The following property can be used to configure cross-origin resource sharing
// in the HTTP nodes.
// See https://github.com/troygoode/node-cors#configuration-options for
// details on its contents. The following is a basic permissive set of options:
httpNodeCors: {
 origin: "*",
 methods: "GET,PUT,POST,DELETE"
},

Jeśli używasz Flask tak samo jak pytanie; musisz najpierw zainstalowaćflask-cors

$ pip install -U flask-cors

Następnie dołącz aplikację Flask cors do swojej aplikacji.

from flask_cors import CORS

Prosta aplikacja będzie wyglądać następująco:

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

@app.route("/")
def helloWorld():
  return "Hello, cross-origin-world!"

Aby uzyskać więcej informacji, możesz sprawdzić dokumentację Flask .

Shady Sherif
źródło
93
i to nie jest bezpieczne
llazzaro,
153
Nie powinieneś wyłączać CORS, ponieważ nie wiesz po co. To okropna odpowiedź.
meagar
124
Chociaż może to nie być bezpieczne, pytanie nie dotyczyło bezpieczeństwa, ale jak wykonać zadanie. Jest to jedna z opcji, z której programista musi wybierać podczas obsługi żądań AJAX między domenami. Pomogło mi to rozwiązać problem, a dla mojej aplikacji nie obchodzi mnie, skąd pochodzą dane. Oczyszczam wszystkie dane wejściowe za pomocą PHP w domenie docelowej, więc jeśli ktoś chce opublikować jakieś śmieci, pozwól mu spróbować. Głównym punktem jest to, że AJAX między domenami może być dozwolony z domeny docelowej. +1 za odpowiedź.
ZurabWeb
23
Chociaż zgadzam się z ogólnym przesłaniem Piero, że nie chodzi konkretnie o bezpieczeństwo, ale bezpieczeństwo jest problemem. Myślę, że powinno to przynajmniej powiedzieć coś takiego: „Zasadniczo jest to złe! Nie rób tego, chyba że wiesz, co robisz! Oto więcej dokumentacji na ten temat: ...”, a może krótko wyjaśnij, dlaczego. Nie chciałbym, żeby ktoś tu przyszedł i pomyślał „Och, mogę po prostu dodać / dostosować ten nagłówek i jestem dobry!” i nie znam pełnych konsekwencji. Chodzi mi o to, że to do nich należy badanie, ale nadal.
Thomas F.
4
Podoba mi się ta odpowiedź ... Mam ten sam problem i to rozwiązuje ... Wyjaśnił, że są pewne problemy z bezpieczeństwem, ale to kolejny problem i pozwól każdemu indywidualnie pomyśleć i rozwiązać ten problem ...
Ari Waisberg,
63

Ponieważ
$ .ajax ({type: „POST” - wywołuje OPCJE
$ .post ( - wywołuje POST

Oba są różne. Listonosz poprawnie nazywa „POST”, ale kiedy go nazwiemy, będzie to „OPCJE”.

W przypadku usług internetowych C # - Web API

Dodaj następujący kod do pliku web.config pod znacznikiem <system.webServer>. To zadziała:

<httpProtocol>
    <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
    </customHeaders>
</httpProtocol>

Upewnij się, że nie popełnisz błędu w wywołaniu Ajax

jQuery

$.ajax({
    url: 'http://mysite.microsoft.sample.xyz.com/api/mycall',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
    },
    type: "POST", /* or type:"GET" or type:"PUT" */
    dataType: "json",
    data: {
    },
    success: function (result) {
        console.log(result);
    },
    error: function () {
        console.log("error");
    }
});

Uwaga: jeśli szukasz pobierania treści ze strony internetowej innej firmy, to ci to nie pomoże . Możesz wypróbować następujący kod, ale nie JavaScript.

System.Net.WebClient wc = new System.Net.WebClient();
string str = wc.DownloadString("http://mysite.microsoft.sample.xyz.com/api/mycall");
George Livingston
źródło
Ta konfiguracja rozwiązała ten sam błąd w Wordpress w Azure Services. Dzięki.
Andre Mesquita
9
Sugeruję użycie określonej wartości początkowej, aby uniknąć żądań z domen zewnętrznych. Na przykład zamiast *używaćhttps://www.myotherdomain.com
pechar
Link hubfly.com/blog/solutions/how-to-fix-angular-4-api-call-issues jest uszkodzony (404).
Peter Mortensen
8

Stosowanie ograniczenia CORS jest funkcją bezpieczeństwa zdefiniowaną przez serwer i wdrożoną przez przeglądarkę .

Przeglądarka przegląda zasady CORS serwera i je przestrzega.

Jednak narzędzie Postman nie przejmuje się polityką CORS serwera.

Dlatego błąd CORS pojawia się w przeglądarce, ale nie w Listonoszu.

Gopinath
źródło
1
Tak, nie mogę wystarczająco podkreślić, dlaczego ten mały szczegół zasługuje na uwagę. Mówiąc o bezpieczeństwie, najważniejsze jest, aby wspomnieć, że CORS jest tak silny, jak klient go wdrażający. Wyobraź sobie więc, że bierzesz prosty HttpClient (kod po stronie serwera) i budujesz serwer proxy, który następnie wykonuje twoje żądania ... Bezpieczeństwo można całkowicie obejść, pozostawiając standard CORS jako kiepskie rozwiązanie
Christopher Bonitz
7

W poniższym dochodzeniu jako API używam http://example.com zamiast http: // myApiUrl / login z twojego pytania, ponieważ to pierwsze działa.

Zakładam, że twoja strona jest na http: //my-site.local: 8088 .

Powodem, dla którego widzisz różne wyniki, jest to, że Listonosz:

  • ustaw nagłówek Host=example.com(twój API)
  • NIE ustawiam nagłówka Origin

Jest to podobne do sposobu wysyłania żądań przez przeglądarkę, gdy witryna i interfejs API mają tę samą domenę (przeglądarki również ustawiają element nagłówka Referer=http://my-site.local:8088, ale nie widzę go w Listonoszu). Gdy Originnagłówek nie jest ustawiony, zazwyczaj serwery domyślnie dopuszczają takie żądania.

Wpisz opis zdjęcia tutaj

Jest to standardowy sposób, w jaki Listonosz wysyła żądania. Ale przeglądarka wysyła żądania inaczej, gdy witryna i interfejs API mają różne domeny , a następnie występuje CORS i przeglądarka automatycznie:

  • ustawia nagłówek Host=example.com(twój jako API)
  • ustawia nagłówek Origin=http://my-site.local:8088(twoja strona)

(Nagłówek Refererma taką samą wartość jak Origin). A teraz na karcie Konsola i sieci Chrome zobaczysz:

Wpisz opis zdjęcia tutaj

Wpisz opis zdjęcia tutaj

Gdy masz Host != Originto CORS, a gdy serwer wykryje takie żądanie, zwykle domyślnie je blokuje .

Origin=nulljest ustawiany po otwarciu treści HTML z katalogu lokalnego i wysyła żądanie. Ta sama sytuacja ma miejsce, gdy wysyłasz żądanie wewnątrz <iframe>, jak w poniższym fragmencie (ale tutaj Hostnagłówek wcale nie jest ustawiony) - ogólnie rzecz biorąc, wszędzie tam, gdzie specyfikacja HTML mówi nieprzejrzyste pochodzenie, możesz to przetłumaczyć Origin=null. Więcej informacji na ten temat można znaleźć tutaj .

fetch('http://example.com/api', {method: 'POST'});
Look on chrome-console > network tab

Jeśli nie korzystasz z prostego żądania CORS, zazwyczaj przeglądarka automatycznie wysyła również żądanie OPTIONS przed wysłaniem żądania głównego - więcej informacji tutaj . Poniższy fragment pokazuje to:

fetch('http://example.com/api', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json'}
});
Look in chrome-console -> network tab to 'api' request.
This is the OPTIONS request (the server does not allow sending a POST request)

Możesz zmienić konfigurację swojego serwera, aby zezwolić na żądania CORS.

Oto przykładowa konfiguracja, która włącza CORS na nginx (plik nginx.conf) - bądź bardzo ostrożny z ustawieniami always/"$http_origin"dla nginx i "*"Apache - spowoduje to odblokowanie CORS z dowolnej domeny.

Oto przykładowa konfiguracja, która włącza CORS na Apache (plik .htaccess)

Kamil Kiełczewski
źródło
2

Wystąpił ten sam błąd w innym przypadku użycia.

Przypadek użycia: w chromie, gdy próbowano wywołać punkt końcowy REST sprężyny pod kątem.

wprowadź opis zdjęcia tutaj

Rozwiązanie: Dodaj adnotację @CrossOrigin („*”) nad odpowiednią klasą kontrolera.

wprowadź opis zdjęcia tutaj

Km
źródło
Używam localhost zamiast * dla bezpieczeństwa
neo7bf
-1

Jeśli używasz platformy .NET jako warstwy środkowej, sprawdź wyraźnie atrybut route, na przykład

Miałem problem, kiedy tak było,

[Route("something/{somethingLong: long}")] //Space.

Naprawiono to przez,

[Route("something/{somethingLong:long}")] //No space
Sai Vaibhav Medavarapu
źródło
-1

Tylko dla projektu .NET Core Web API dodaj następujące zmiany:

  1. Dodaj następujący kod po services.AddMvc()wierszu w ConfigureServices()metodzie pliku Startup.cs:
services.AddCors(allowsites=>{allowsites.AddPolicy("AllowOrigin", options => options.AllowAnyOrigin());
            });
  1. Dodaj następujący kod po app.UseMvc()wierszu w Configure()metodzie pliku Startup.cs:
app.UseCors(options => options.AllowAnyOrigin());
  1. Otwórz kontroler, do którego chcesz uzyskać dostęp poza domeną, i dodaj ten atrybut na poziomie kontrolera:
[EnableCors("AllowOrigin")]
Shakti Srivastav
źródło