Ustawianie kontroli dostępu-Allow-Origin w ASP.Net MVC - najprostsza możliwa metoda

206

Mam prostą metodę działania, która zwraca trochę JSON. Działa na ajax.example.com. Potrzebuję uzyskać dostęp do tego z innej witryny someothersite.com.

Jeśli spróbuję to nazwać, otrzymuję oczekiwane ...:

Origin http://someothersite.com is not allowed by Access-Control-Allow-Origin.

Znam dwa sposoby obejścia tego: JSONP i utworzenie niestandardowego HttpHandler w celu ustawienia nagłówka.

Czy nie ma prostszego sposobu?

Czy nie jest możliwe proste działanie, aby zdefiniować listę dozwolonych źródeł - lub po prostu zezwolić wszystkim? Może filtr akcji?

Optymalne byłoby ...:

return json(mydata, JsonBehaviour.IDontCareWhoAccessesMe);
Kjensen
źródło
1
Proszę spojrzeć tutaj na vNext i MVC6: neelbhatt40.wordpress.com/2015/09/10/…
Neel

Odpowiedzi:

381

Dla zwykłych kontrolerów ASP.NET MVC

Utwórz nowy atrybut

public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.RequestContext.HttpContext.Response.AddHeader("Access-Control-Allow-Origin", "*");
        base.OnActionExecuting(filterContext);
    }
}

Oznacz swoją akcję:

[AllowCrossSiteJson]
public ActionResult YourMethod()
{
    return Json("Works better?");
}

Dla interfejsu API sieci Web ASP.NET

using System;
using System.Web.Http.Filters;

public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        if (actionExecutedContext.Response != null)
            actionExecutedContext.Response.Headers.Add("Access-Control-Allow-Origin", "*");

        base.OnActionExecuted(actionExecutedContext);
    }
}

Oznacz cały kontroler API:

[AllowCrossSiteJson]
public class ValuesController : ApiController
{

Lub indywidualne wywołania API:

[AllowCrossSiteJson]
public IEnumerable<PartViewModel> Get()
{
    ...
}

Dla Internet Explorera <= v9

IE <= 9 nie obsługuje CORS. Napisałem javascript, który automatycznie przekieruje te żądania przez serwer proxy. Wszystko jest w 100% przejrzyste (wystarczy podać mój serwer proxy i skrypt).

Pobierz go za pomocą nugetu corsproxyi postępuj zgodnie z dołączonymi instrukcjami.

Wpis na blogu | Kod źródłowy

jgauffin
źródło
8
Niesamowity! I luv MVC + U!
Piotr Kula,
2
pod wrażeniem elegancji tego rozwiązania
BraveNewMath,
3
Możesz z łatwością rozszerzyć atrybut, aby zaakceptować określone pochodzenie, jeśli chcesz ograniczyć CORS do własnych domen.
Petrus Theron
2
Powinieneś być w stanie dodać to do RegisterHttpFilters w App_Start \ FilterConfig, prawda? Spowodowałoby to zastosowanie go do wszystkich kontrolerów API w projekcie. Łącząc to z komentarzem Pate powyżej, możesz ograniczyć CORS do swojej domeny (domen) dla wszystkich kontrolerów.
bdwakefield
9
Niedawno zaktualizowałem nasz projekt do MVC 5 i próbowałem to zrobić. Nawet dodanie nagłówka w filtrze nie działa. Kiedy przeglądam żądanie w sieci, w odpowiedzi nie ma nagłówka. Czy jest coś jeszcze, co należy zrobić, aby to zadziałało?
Kneemin
121

Jeśli używasz IIS 7+, możesz umieścić plik web.config w katalogu głównym folderu, korzystając z tego w sekcji system.webServer:

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

Zobacz: http://msdn.microsoft.com/en-us/library/ms178685.aspx I: http://enable-cors.org/#how-iis7

LaundroMatt
źródło
1
Nie pamiętam już, dlaczego, ale ta metoda nie zawsze działa w IIS 7+
LaundroMatt
Hmm Jedyny powód, dla którego mogę pomyśleć, że to nie zadziała, jeśli żądanie pochodzi z przeglądarki innej niż CORS. Ale będę kontynuować śledztwo.
sellmeadog
29
Dzięki temu cała strona będzie przyjazna dla CORS. Jeśli ktoś chce oznaczyć tylko jedną akcję lub kontroler jako przyjazny dla CORS, wówczas zaakceptowana odpowiedź jest znacznie lepsza.
Lev Dubinets,
1
Jeśli zobaczysz sekcję ASP.Net , zawiera ona podpowiedź : „Uwaga: to podejście jest zgodne z IIS6, IIS7 Classic Mode i IIS7 Integrated Mode”.
dostbus
1
Podczas publikowania aplikacji w środowisku SharePoint mam do czynienia z problemem między domenami. Kiedy uruchamiam moją aplikację w środowisku lokalnym, moja aplikacja działa poprawnie, ale kiedy publikuję ją na lazurach w mojej witrynie Sharepoint, przekierowuje do strony błędów w wywołaniu formularza Ajax.Begin. Wypróbowałem to rozwiązanie, ale nie działa dla mnie. Czy jest jakaś inna alternatywa?
Jyotsna Wadhwani,
22

Natknąłem się na problem polegający na tym, że przeglądarka odmówiła podania treści, którą pobrała, gdy żądanie przeszło w plikach cookie (np. Xhr miał swoją withCredentials=true), a witryna została Access-Control-Allow-Originustawiona *. (Błąd w Chrome brzmiał: „Nie można użyć symboli wieloznacznych w Access-Control-Allow-Origin, gdy flaga poświadczeń jest prawdziwa”).

Opierając się na odpowiedzi z @jgauffin, stworzyłem to, co jest w zasadzie sposobem obejścia tego konkretnego sprawdzenia bezpieczeństwa przeglądarki, więc zastrzegaj emptor.

public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // We'd normally just use "*" for the allow-origin header, 
        // but Chrome (and perhaps others) won't allow you to use authentication if
        // the header is set to "*".
        // TODO: Check elsewhere to see if the origin is actually on the list of trusted domains.
        var ctx = filterContext.RequestContext.HttpContext;
        var origin = ctx.Request.Headers["Origin"];
        var allowOrigin = !string.IsNullOrWhiteSpace(origin) ? origin : "*";
        ctx.Response.AddHeader("Access-Control-Allow-Origin", allowOrigin);
        ctx.Response.AddHeader("Access-Control-Allow-Headers", "*");
        ctx.Response.AddHeader("Access-Control-Allow-Credentials", "true");
        base.OnActionExecuting(filterContext);
    }
}
Ken Smith
źródło
To było szczególnie przydatne, dziękuję.
cklimowski
15

To jest naprawdę proste, po prostu dodaj to w web.config

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <add name="Access-Control-Allow-Origin" value="http://localhost" />
      <add name="Access-Control-Allow-Headers" value="X-AspNet-Version,X-Powered-By,Date,Server,Accept,Accept-Encoding,Accept-Language,Cache-Control,Connection,Content-Length,Content-Type,Host,Origin,Pragma,Referer,User-Agent" />
      <add name="Access-Control-Allow-Methods" value="GET, PUT, POST, DELETE, OPTIONS" />
      <add name="Access-Control-Max-Age" value="1000" />
    </customHeaders>
  </httpProtocol>
</system.webServer>

W Origin umieść wszystkie domeny, które mają dostęp do twojego serwera WWW, w nagłówkach umieść wszystkie możliwe nagłówki, których może użyć każde żądanie HTTP w ajax, w metodach umieść wszystkie metody, na które zezwolisz na serwerze

pozdrowienia :)

Zvonimir Tokic
źródło
Dodanie „Autoryzacji” w nagłówkach Kontrola dostępu-Zezwalaj może być również przydatne, jeśli zamierzasz używać zapytań autoryzowanych.
AFract
9

Czasami OPCJE czasownik również powoduje problemy

Po prostu: zaktualizuj plik web.config za pomocą następujących elementów

<system.webServer>
    <httpProtocol>
        <customHeaders>
          <add name="Access-Control-Allow-Origin" value="*" />
          <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
        </customHeaders>
    </httpProtocol>
</system.webServer>

I zaktualizuj nagłówki usługi internetowej / kontrolera za pomocą httpGet i httpOptions

// GET api/Master/Sync/?version=12121
        [HttpGet][HttpOptions]
        public dynamic Sync(string version) 
        {
Bishoy Hanna
źródło
BTW, w sitefinity musisz dodać * do zaawansowanych ustawień systemu w sekcji bezpieczeństwa
Bishoy Hanna
w jakich plikach muszę zaktualizować nagłówki kontrolera?
user3281466,
5

Dodaj ten wiersz do metody, jeśli używasz interfejsu API.

HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*"); 
Gopichandar
źródło
4

Ten samouczek jest bardzo przydatny. Aby podać krótkie podsumowanie:

  1. Skorzystaj z pakietu CORS dostępnego w Nuget: Install-Package Microsoft.AspNet.WebApi.Cors

  2. W swoim WebApiConfig.cspliku dodaj config.EnableCors()do Register()metody.

  3. Dodaj atrybut do kontrolerów, których potrzebujesz do obsługi corsów:

[EnableCors(origins: "<origin address in here>", headers: "*", methods: "*")]

GrandMasterFlush
źródło
Musiałem użyć tej metody, ponieważ musiałem ustawić niestandardowy nagłówek w moim żądaniu, a metoda niestandardowego atrybutu nie działała z żądaniem przed lotem przeglądarki. Wydaje się, że działa to we wszystkich przypadkach.
lehn0058
3
    public ActionResult ActionName(string ReqParam1, string ReqParam2, string ReqParam3, string ReqParam4)
    {
        this.ControllerContext.HttpContext.Response.Headers.Add("Access-Control-Allow-Origin","*");
         /*
                --Your code goes here --
         */
        return Json(new { ReturnData= "Data to be returned", Success=true }, JsonRequestBehavior.AllowGet);
    }
Pranav Labhe
źródło
2

Istnieją różne sposoby przekazywania nagłówków kontroli dostępu i kontroli dostępu.

  • Jak wyjaśnił jgauffin, możemy utworzyć nowy atrybut.
  • Jak wyjaśnił LaundroMatt, możemy dodać plik web.config.
  • Innym sposobem jest dodanie kodu jak poniżej w pliku webApiconfig.cs.

    config.EnableCors (nowe EnableCorsAttribute („ ”, nagłówki: „ ”, metody: „*”, odsłonięte nagłówki: „TestHeaderToExpose”) {SupportsCredentials = true});

Lub możemy dodać poniższy kod w pliku Global.Asax.

protected void Application_BeginRequest()
        {
            if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
            {
                //These headers are handling the "pre-flight" OPTIONS call sent by the browser
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "*");
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Credentials", "true");
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "http://localhost:4200");
                HttpContext.Current.Response.AddHeader("Access-Control-Expose-Headers", "TestHeaderToExpose");
                HttpContext.Current.Response.End();
            }
        }

Napisałem to dla opcji. Proszę zmodyfikować to samo, jak na swoje potrzeby.

Happy Coding !!

Trilok Pathak
źródło
1

Po całym zmaganiu przez cały wieczór w końcu udało mi się to uruchomić. Po pewnym debugowaniu odkryłem, że problem, w który wchodzę, polegał na tym, że mój klient wysyłał tak zwane żądanie opcji inspekcji wstępnej, aby sprawdzić, czy aplikacja może wysłać żądanie postu z podaniem źródła, metod i nagłówków. Nie chciałem używać Owina ani kontrolera APIC, więc zacząłem kopać i wymyśliłem następujące rozwiązanie z tylko ActionFilterAttribute. Szczególnie ważna jest część „Kontrola dostępu - Zezwalaj na nagłówki”, ponieważ wspomniane tam nagłówki muszą pasować do nagłówków, które wyśle ​​twoje zapytanie.

using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MyNamespace
{
    public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            HttpRequest request = HttpContext.Current.Request;
            HttpResponse response = HttpContext.Current.Response;

            // check for preflight request
            if (request.Headers.AllKeys.Contains("Origin") && request.HttpMethod == "OPTIONS")
            {
                response.AppendHeader("Access-Control-Allow-Origin", "*");
                response.AppendHeader("Access-Control-Allow-Credentials", "true");
                response.AppendHeader("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE");
                response.AppendHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, X-RequestDigest, Cache-Control, Content-Type, Accept, Access-Control-Allow-Origin, Session, odata-version");
                response.End();
            }
            else
            {
                HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
                HttpContext.Current.Response.Cache.SetNoStore();

                response.AppendHeader("Access-Control-Allow-Origin", "*");
                response.AppendHeader("Access-Control-Allow-Credentials", "true");
                if (request.HttpMethod == "POST")
                {
                    response.AppendHeader("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE");
                    response.AppendHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, X-RequestDigest, Cache-Control, Content-Type, Accept, Access-Control-Allow-Origin, Session, odata-version");
                }

                base.OnActionExecuting(filterContext);
            }
        }
    }
}

Wreszcie moja metoda akcji MVC wygląda następująco. Ważne jest również wspomnienie o Opcjach HttpVerbs, ponieważ w przeciwnym razie żądanie inspekcji wstępnej zakończy się niepowodzeniem.

[AcceptVerbs(HttpVerbs.Post | HttpVerbs.Options)]
[AllowCrossSiteJson]
public async Task<ActionResult> Create(MyModel model)
{
    return Json(await DoSomething(model));
}
pkmelee337
źródło
0

W Web.config wprowadź następujące dane

<system.webServer>
<httpProtocol>
  <customHeaders>
    <clear />     
    <add name="Access-Control-Allow-Credentials" value="true" />
    <add name="Access-Control-Allow-Origin" value="http://localhost:123456(etc)" />
  </customHeaders>
</httpProtocol>
Elvis Skensberg
źródło
0

Jeśli używasz IIS, sugeruję wypróbowanie modułu CORS IIS .
Jest łatwy w konfiguracji i działa na wszystkich typach kontrolerów.

Oto przykład konfiguracji:

    <system.webServer>
        <cors enabled="true" failUnlistedOrigins="true">
            <add origin="*" />
            <add origin="https://*.microsoft.com"
                 allowCredentials="true"
                 maxAge="120"> 
                <allowHeaders allowAllRequestedHeaders="true">
                    <add header="header1" />
                    <add header="header2" />
                </allowHeaders>
                <allowMethods>
                     <add method="DELETE" />
                </allowMethods>
                <exposeHeaders>
                    <add header="header1" />
                    <add header="header2" />
                </exposeHeaders>
            </add>
            <add origin="http://*" allowed="false" />
        </cors>
    </system.webServer>
olsh
źródło
0

Używam DotNet Core MVC i po kilku godzinach walki z pakietami nuget, Startup.cs, atrybutami i tym miejscem, po prostu dodałem to do akcji MVC:

Response.Headers.Add("Access-Control-Allow-Origin", "*");

Zdaję sobie sprawę, że jest to dość niezręczne, ale to wszystko, czego potrzebowałem i nic więcej nie chciałem dodawać tych nagłówków. Mam nadzieję, że to pomaga komuś innemu!

Ben Power
źródło