Nieautoryzowane wywołanie strony internetowej zwracającej stronę logowania zamiast 401

180

Jak skonfigurować mój projekt mvc / webapi, aby metoda webapi wywoływana z widoku brzytwy nie zwracała strony logowania, gdy jest nieautoryzowana?

Jest to aplikacja MVC5, która ma również kontrolery WebApi do połączeń przez javascript.

Dwie metody poniżej

[Route("api/home/LatestProblems")]      
[HttpGet()]
public List<vmLatestProblems> LatestProblems()
{
    // Something here
}

[Route("api/home/myLatestProblems")]
[HttpGet()]
[Authorize(Roles = "Member")]
public List<vmLatestProblems> mylatestproblems()
{
   // Something there
}

są wywoływane za pomocą następującego kodu kątowego:

angular.module('appWorship').controller('latest', 
    ['$scope', '$http', function ($scope,$http) {         
        var urlBase = baseurl + '/api/home/LatestProblems';
        $http.get(urlBase).success(function (data) {
            $scope.data = data;
        }).error(function (data) {
            console.log(data);
        });
        $http.get(baseurl + '/api/home/mylatestproblems')
          .success(function (data) {
            $scope.data2 = data;
        }).error(function (data) {
            console.log(data);
        });  
    }]
);

Więc nie jestem zalogowany, a pierwsza metoda z powodzeniem zwraca dane. druga metoda zwraca (w funkcji sukcesu) dane, które zawierają odpowiednik strony logowania. tzn. co dostaniesz w mvc, jeśli poprosisz o akcję kontrolera, która została opatrzona opcją [Autoryzuj] i nie byłeś zalogowany.

Chcę, aby zwracał 401 bez upoważnienia, dzięki czemu mogę wyświetlać różne dane dla użytkowników na podstawie tego, czy są zalogowani, czy nie. Idealnie, jeśli użytkownik jest zalogowany, chcę mieć dostęp do właściwości Użytkownik kontrolera, aby móc zwrócić dane właściwe temu członkowi.

AKTUALIZACJA: Ponieważ żadna z poniższych sugestii wydaje się już nie działać (zmiany w Tożsamości lub WebAPI), stworzyłem surowy przykład na github, który powinien zilustrować problem.

Tim
źródło

Odpowiedzi:

78

Istnieją dwie implementacje AuthorizeAttribute i musisz upewnić się, że odwołujesz się do poprawnej dla interfejsów API sieci Web. Istnieje System.Web.Http.AuthorizeAttribute, który jest używany dla interfejsów API sieci Web, i System.Web.Mvc.AuthorizeAttribute, który jest używany dla kontrolerów z widokami. Http.AuthorizeAttribute zwróci błąd 401, jeśli autoryzacja się nie powiedzie i Mvc.AuthorizeAttribute przekieruje na stronę logowania.

Zaktualizowano 26.11.2013

Wygląda więc na to, że w MVC 5 wszystko zmieniło się drastycznie, jak zauważył Brock Allen w swoim artykule . Wydaje mi się, że potok OWIN przejmuje i wprowadza nowe zachowanie. Teraz, gdy użytkownik nie jest autoryzowany, zwracany jest status 200 z następującymi informacjami w nagłówku HTTP.

X-Responded-JSON: {"status":401,"headers":{"location":"http:\/\/localhost:59540\/Account\/Login?ReturnUrl=%2Fapi%2FTestBasic"}}

Możesz zmienić logikę po stronie klienta, aby sprawdzić te informacje w nagłówku i ustalić, jak sobie z tym poradzić, zamiast szukać statusu 401 w gałęzi błędów.

Próbowałem zastąpić to zachowanie w niestandardowym atrybucie AuthorizeAttribute , ustawiając status w odpowiedzi w metodach OnAuthorization i HandleUnauthorizedRequest .

actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);

Ale to nie zadziałało. Nowy potok musi pobrać tę odpowiedź później i zmodyfikować ją tak, aby uzyskać taką samą odpowiedź, jaką otrzymywałem wcześniej. Zgłoszenie wyjątku HttpException również nie działało, ponieważ zostało zmienione na status błędu 500.

Przetestowałem rozwiązanie Brocka Allena i zadziałało, gdy korzystałem z wywołania jQuery ajax. Jeśli to nie działa, zgaduję, że to dlatego, że używasz kątowej. Uruchom test za pomocą aplikacji Fiddler i sprawdź, czy w nagłówku znajduje się następujący tekst.

X-Requested-With: XMLHttpRequest

Jeśli tak nie jest, to jest to problem. Nie jestem zaznajomiony z angular, ale jeśli pozwala ci wstawić własne wartości nagłówka, dodaj to do twoich żądań ajax i prawdopodobnie zacznie działać.

Kevin Junghans
źródło
Myślę, że używam System.web.http.authorizeattribute, przynajmniej ten webapicontroller nie ma zastosowania dla system.web.mvc, a przejście do definicji atrybutu autoryzacji wysyła mnie do system.web.http
Tim
Cześć @ kevin-junghans jest tu całkowicie zdezorientowany. powyższy przykład z Shiva używa atrybutu autoryzacji mvc, który z pewnością nie powinienem stosować do akcji webapi. Przykład Brocka Allena nie działa albo nie wydaje się, że jest to żądanie ajax, gdy przechodzę przez.
Tim
1
tylko dostrzegłem tę odpowiedź (myśl, że przepełnienie stosu nie wysyła powiadomień) Dodałem przykład github, aby zilustrować problem, a teraz dodałem poprawkę do kątowych nagłówków. Dzięki. Nie wydaje się jednak słuszne, że w atrybucie autoryzacji nie ma właściwości, którą mogę sprawdzić, lub wspomniana oryginalna funkcjonalność już nie działa.
Tim
3
Używanie POSTMAN i paramera nagłówka X-Requested-With: XMLHttpRequest działa dla mnie ... dzięki
chemitaxis
A co, jeśli masz to, co zamierzasz robić jako czysty interfejs API sieci Web? Pracuję nad projektem, który został skonfigurowany przez inną osobę, a autoryzacja przekierowuje zgodnie z opisem tutaj, ale mam inny projekt API, który działa dobrze. Musi być coś, co sprawia, że ​​wydaje się, że jest to aplikacja MVC, a nie aplikacja API, ale nie mogę znaleźć tego, co mogłoby ją zepsuć.
Derek Greer
123

Brock Allen ma fajny post na blogu o tym, jak zwrócić 401 dla wywołań ajax przy użyciu uwierzytelniania Cookie i OWIN. http://brockallen.com/2013/10/27/using-cookie-authentication-middleware-with-web-api-and-401-response-codes/

Umieść to w metodzie ConfigureAuth w pliku Startup.Auth.cs:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
  AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
  LoginPath = new PathString("/Account/Login"),
  Provider = new CookieAuthenticationProvider
  {
    OnApplyRedirect = ctx =>
    {
      if (!IsAjaxRequest(ctx.Request))
      {
        ctx.Response.Redirect(ctx.RedirectUri);
      }
    }
  }
});

private static bool IsAjaxRequest(IOwinRequest request)
{
  IReadableStringCollection query = request.Query;
  if ((query != null) && (query["X-Requested-With"] == "XMLHttpRequest"))
  {
     return true;
  }
  IHeaderDictionary headers = request.Headers;
  return ((headers != null) && (headers["X-Requested-With"] == "XMLHttpRequest"));
}
Olav Nybø
źródło
68
Odmiana na ten temat: jeśli wszystkie wywołania interfejsu API sieci Web przechodzą określoną ścieżkę, np. /apiMożesz użyć tej ścieżki do ustalenia, czy przekierować. Jest to szczególnie przydatne, jeśli masz klientów korzystających z innych formatów, takich jak JSON. Wymień wezwanie do IsAjaxRequestz if (!context.Request.Path.StartsWithSegments(new PathString("/api"))).
Edward Brey,
Późno na imprezę, ale ta metoda jest dla mnie jedyna i wydaje się bardziej „dokładna”.
Stephen Collins
Nawet późno (r) na imprezę, ale okazało się to bardzo przydatne ... denerwuje mnie, że domyślnie wygenerowany kod robi to tak źle, w tak frustrująco trudny sposób debugowania.
Nick
Jeśli szukasz rozwiązania WebApi, odpowiedź Manika jest dobrą alternatywą dla wysoko ocenionego komentarza tutaj.
Dunc,
5
Używając C # 6, oto mniejsza wersja IsAjaxRequest: private static bool IsAjaxRequest(IOwinRequest request) { return request.Query?["X-Requested-With"] == "XMLHttpRequest" || request.Headers?["X-Requested-With"] == "XMLHttpRequest"; }
Peter Örneholm
85

Jeśli dodajesz asp.net WebApi na stronie asp.net MVC, prawdopodobnie chcesz odpowiedzieć nieautoryzowanym na niektóre żądania. Ale wtedy wkracza infrastruktura ASP.NET, a gdy spróbujesz ustawić kod statusu odpowiedzi na HttpStatusCode. Nieautoryzowane, otrzymasz 302 przekierowanie na stronę logowania.

Jeśli używasz tożsamości asp.net i uwierzytelniania opartego na owin, kod, który może pomóc rozwiązać ten problem:

public void ConfigureAuth(IAppBuilder app)
{
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString("/Account/Login"),
        Provider = new CookieAuthenticationProvider()
        {
            OnApplyRedirect = ctx =>
            {
                if (!IsApiRequest(ctx.Request))
                {
                    ctx.Response.Redirect(ctx.RedirectUri);
                }
            }
        }
    });

    app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}


private static bool IsApiRequest(IOwinRequest request)
{
    string apiPath = VirtualPathUtility.ToAbsolute("~/api/");
    return request.Uri.LocalPath.StartsWith(apiPath);
}
Manik Arora
źródło
1
zmieniłem dyskryminatora, aby sprawdzić, czy żądania akceptują tekst / html lub application / xhtml jako odpowiedź, jeśli nie, zakładam, że jest to „zautomatyzowane” żądanie klienta, takie żądanie ajax
L.Trabacchin
4
Ja też wolę to podejście. Jedynym dodatkiem, który zrobiłem, była konwersja LocalPath .ToLower () na wypadek, gdyby zażądali „/ API” lub czegoś takiego.
FirstDivision,
1
Wielkie dzięki. Uratowało mi to dzień. :)
Amit Kumar
Czy ktoś ma z tym szczęście? CookieAuthenticationOptions nie ma już właściwości Provider od rdzenia aspnet 1.1.
Jeremy,
27

Mam taką samą sytuację, w której OWIN zawsze przekierowuje odpowiedź 401 na stronę logowania z WebApi. Nasz interfejs API sieci Web obsługuje nie tylko połączenia ajax z Angular, ale także połączenia mobilne, Win Form. Dlatego rozwiązanie sprawdzające, czy żądanie jest żądaniem ajax, nie jest tak naprawdę posortowane w naszym przypadku.

Zdecydowałem, że innym podejściem jest wstrzyknięcie nowej odpowiedzi nagłówka: Suppress-Redirectjeśli odpowiedzi pochodzą z webApi. Implementacja dotyczy modułu obsługi:

public class SuppressRedirectHandler : DelegatingHandler
{
    /// <summary>
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return base.SendAsync(request, cancellationToken).ContinueWith(task =>
        {
            var response = task.Result;
            response.Headers.Add("Suppress-Redirect", "True");
            return response;
        }, cancellationToken);
    }
}

I zarejestruj ten moduł obsługi na poziomie globalnym WebApi:

config.MessageHandlers.Add(new SuppressRedirectHandler());

Tak więc podczas uruchamiania OWIN możesz sprawdzić, czy nagłówek odpowiedzi ma Suppress-Redirect:

public void Configuration(IAppBuilder app)
{
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationMode = AuthenticationMode.Active,
        AuthenticationType = DefaultApplicationTypes.ApplicationCookie,
        ExpireTimeSpan = TimeSpan.FromMinutes(48),

        LoginPath = new PathString("/NewAccount/LogOn"),

        Provider = new CookieAuthenticationProvider()
        {
            OnApplyRedirect = ctx =>
            {
                var response = ctx.Response;
                if (!IsApiResponse(ctx.Response))
                {
                    response.Redirect(ctx.RedirectUri);
                }
            }
        }
    });
}

private static bool IsApiResponse(IOwinResponse response)
{
    var responseHeader = response.Headers;

    if (responseHeader == null) 
        return false;

    if (!responseHeader.ContainsKey("Suppress-Redirect"))
        return false;

    if (!bool.TryParse(responseHeader["Suppress-Redirect"], out bool suppressRedirect))
        return false;

    return suppressRedirect;
}
cuongle
źródło
Dziękuję Ci ! Nasze interfejsy API działały na każdej platformie, z wyjątkiem Xamarin / Android. Użyje tego rozwiązania
Jurion
17

W poprzednich wersjach ASP.NET musiałeś robić całą masę rzeczy aby to zadziałało.

Dobrą wiadomością jest to, że używasz ASP.NET 4.5. możesz wyłączyć przekierowanie uwierzytelniania formularzy za pomocą nowego HttpResponse.SuppressFormsAuthenticationRedirect właściwości .

W Global.asax:

protected void Application_EndRequest(Object sender, EventArgs e)
{
        HttpApplication context = (HttpApplication)sender;
        context.Response.SuppressFormsAuthenticationRedirect = true;
}

EDYCJA : Możesz także rzucić okiem na ten artykuł rzucić Siergieja Zwezdina, który ma bardziej wyrafinowany sposób na osiągnięcie tego, co próbujesz zrobić.

Odpowiednie fragmenty kodu i narracja autora wklejone poniżej. Oryginalny autor kodu i narracji - Sergey Zwezdin .

Po pierwsze - ustalmy, czy bieżące żądanie HTTP jest żądaniem AJAX. Jeśli tak, powinniśmy wyłączyć zastępowanie HTTP 401 HTTP 302:

public class ApplicationAuthorizeAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        var httpContext = filterContext.HttpContext;
        var request = httpContext.Request;
        var response = httpContext.Response;

        if (request.IsAjaxRequest())
            response.SuppressFormsAuthenticationRedirect = true;

        base.HandleUnauthorizedRequest(filterContext);
    }
}

Po drugie - dodajmy warunek :: jeśli użytkownik zostanie uwierzytelniony, wtedy wyślemy HTTP 403; i HTTP 401 w przeciwnym razie.

public class ApplicationAuthorizeAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        var httpContext = filterContext.HttpContext;
        var request = httpContext.Request;
        var response = httpContext.Response;
        var user = httpContext.User;

        if (request.IsAjaxRequest())
        {
            if (user.Identity.IsAuthenticated == false)
                response.StatusCode = (int)HttpStatusCode.Unauthorized;
            else
                response.StatusCode = (int)HttpStatusCode.Forbidden;

            response.SuppressFormsAuthenticationRedirect = true;
            response.End();
        }

        base.HandleUnauthorizedRequest(filterContext);
    }
}

Dobra robota. Teraz powinniśmy zastąpić wszystkie zastosowania standardowego AuthorizeAttribute tym nowym filtrem. Może nie mieć zastosowania do sime facetów, którzy są estetami kodu. Ale nie znam innego sposobu. Jeśli masz, przejdźmy do komentarzy.

Ostatni, co powinniśmy zrobić - dodać obsługę HTTP 401/403 po stronie klienta. Możemy użyć ajaxError w jQuery, aby uniknąć duplikacji kodu:

$(document).ajaxError(function (e, xhr) {
    if (xhr.status == 401)
        window.location = "/Account/Login";
    else if (xhr.status == 403)
        alert("You have no enough permissions to request this resource.");
});

Wynik -

  • Jeśli użytkownik nie zostanie uwierzytelniony, po każdym wywołaniu AJAX zostanie przekierowany na stronę logowania.
  • Jeśli użytkownik jest uwierzytelniony, ale nie ma wystarczających uprawnień, zobaczy przyjazny komunikat o błędzie.
  • Jeśli użytkownik jest uwierzytelniony i ma wystarczające uprawnienia, nie ma żadnych błędów, a żądanie HTTP będzie kontynuowane jak zwykle.
siedmiodniowa żałoba
źródło
Im używam nowej struktury tożsamości do uwierzytelniania za pośrednictwem mvc. Czy to ustawienie nie uniemożliwiłoby logowania mvc, a także wywołań webapi?
Tim
5
kiedy sprawdziłem ten przykład, wygląda na to, że używany atrybut autoryzacji jest wersją MVC, a nie wersją WebApi. jednak wersja webapi nie ma opcji tłumienia uwierzytelniania formularzy.
Tim
moje żądanie nie ma metody IsAjaxRequest.
Tim
1
Tim popatrzy na to dla IsAjaxRequest: brockallen.com/2013/10/27/… Jeśli używasz AngularJs bez edycji nagłówków, nie będziesz mieć „XMLHttpRequest” i albo go dodasz, albo sprawdzisz coś innego.
Tim
10

Jeśli uruchamiasz swój projekt Web APIw swoim MVCprojekcie, musisz utworzyć niestandardowy, AuthorizeAttributeaby zastosować go do swoich APImetod. W ramach IsAuthorized overridemusisz złapać prąd, HttpContextaby zapobiec przekierowaniu, tak jak to:

    protected override bool IsAuthorized(HttpActionContext actionContext)
    {
        if (string.IsNullOrWhiteSpace(Thread.CurrentPrincipal.Identity.Name))
        {
            var response = HttpContext.Current.Response;
            response.SuppressFormsAuthenticationRedirect = true;
            response.StatusCode = (int)System.Net.HttpStatusCode.Forbidden;
            response.End();
        }

        return base.IsAuthorized(actionContext);
    }
Serj Sagan
źródło
8

Używając integracji z Azure Active Directory, podejście wykorzystujące CookieAuthenticationoprogramowanie pośrednie nie działało dla mnie. Musiałem wykonać następujące czynności:

app.UseOpenIdConnectAuthentication(
    new OpenIdConnectAuthenticationOptions
    {
        ...
        Notifications = new OpenIdConnectAuthenticationNotifications
        {   
            ...         
            RedirectToIdentityProvider = async context =>
            {
                if (!context.Request.Accept.Contains("html"))
                {
                    context.HandleResponse();
                }
            },
            ...
        }
    });

Jeśli żądanie pochodzi z samej przeglądarki (a nie na przykład wywołania AJAX), nagłówek Accept będzie zawierać ciąg html . Tylko wtedy, gdy klient zaakceptuje HTML, uważam przekierowanie za przydatne.

Moja aplikacja kliencka może obsłużyć 401, informując użytkownika, że ​​aplikacja nie ma już dostępu i musi się ponownie załadować, aby zalogować się ponownie.

Dave Van den Eynde
źródło
Jest to bardzo podobne do rozwiązania zaproponowanego dla pokrewnego pytania: stackoverflow.com/questions/34997674/...
Guillaume LaHaye
6

Miałem także aplikację MVC5 (System.Web) z WebApi (używając OWIN) i chciałem tylko zapobiec zmianie 401 odpowiedzi z WebApi na 302 odpowiedzi.

Dla mnie zadziałało stworzenie niestandardowej wersji WebApi AuthorizeAttribute w następujący sposób:

public class MyAuthorizeAttribute : System.Web.Http.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
    {
        base.HandleUnauthorizedRequest(actionContext);
        HttpContext.Current.Response.SuppressFormsAuthenticationRedirect = true;
    }
}

I używać go zamiast standardowego atrybutu autoryzacji WebApi. Użyłem standardowego MVC AuthorizeAttribute, aby zachować zachowanie MVC bez zmian.

Jono Job
źródło
Działa, ale teraz mam problem z tym, że klient otrzymuje status -1 zamiast 401
Sebastián Rojas
@ SebastiánRojas Nie jestem pewien, co by to spowodowało - ustawienie SuppressFormsAuthenticationRedirect flagi spowodowało, że po prostu zwrócił mi istniejącą 401.
Jono Job
3

Wystarczy zainstalować następujący pakiet NeGet

Zainstaluj pakiet Microsoft.AspNet.WebApi.Owin

Napisz następujący kod w pliku WebApiConfig.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        //Web API configuration and services
        //Configure Web API to use only bearer token authentication.
        config.SuppressDefaultHostAuthentication();
        config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
        config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
        config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("multipart/form-data"));
    }
}

źródło
Wszystko, co musiałem zrobić, to dodać ten filtr, a jego działanie w config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));przeciwnym razie User.Identity.IsAuthenticatedjest zawszefalse
Ricardo Saracino,
1

jeśli chcesz złapać Content-Type == application / json, możesz użyć tego kodu:

private static bool IsAjaxRequest(IOwinRequest request)
    {
        IReadableStringCollection queryXML = request.Query;
        if ((queryXML != null) && (queryXML["X-Requested-With"] == "XMLHttpRequest"))
        {
            return true;
        }

        IReadableStringCollection queryJSON = request.Query;
        if ((queryJSON != null) && (queryJSON["Content-Type"] == "application/json"))
        {
            return true;
        }

        IHeaderDictionary headersXML = request.Headers;
        var isAjax = ((headersXML != null) && (headersXML["X-Requested-With"] == "XMLHttpRequest"));

        IHeaderDictionary headers = request.Headers;
        var isJson = ((headers != null) && (headers["Content-Type"] == "application/json"));

        return isAjax || isJson;

    }

pozdrowienia!!

chemitaksja
źródło
1

Trudno mi było uzyskać zarówno kod stanu, jak i odpowiedź tekstową, pracując w metodach OnAuthorization / HandleUnauthorizedRequest. To okazało się dla mnie najlepszym rozwiązaniem:

    actionContext.Response = new HttpResponseMessage()
    {
        StatusCode = HttpStatusCode.Forbidden,
        Content = new StringContent(unauthorizedMessage)
    };
PutoTropical
źródło
1

Po wielu staraniach, aby uniknąć przekierowań na stronę logowania, zdałem sobie sprawę, że jest to w rzeczywistości całkiem odpowiednie dla atrybutu Autoryzuj. To znaczy idź i uzyskaj autoryzację. Zamiast połączeń api, które nie są autoryzowane, chciałem po prostu nie ujawniać żadnych informacji hakerom. Ten cel łatwiej było osiągnąć bezpośrednio, dodając nowy atrybut wyprowadzony z funkcji Autoryzuj, który zamiast tego ukrywa zawartość jako błąd 404:

public class HideFromAnonymousUsersAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
    {
         actionContext.Response = ActionContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, "Access Restricted");
    }
}
użytkownik3879365
źródło
1

Mieszanie MVC i WebAPI, jeśli żądanie jest nieautoryzowane, przekieruje do strony logowania nawet w żądaniu WebAPI. W tym celu możemy dodać poniższy kod, aby wysłać odpowiedź na aplikację mobilną

protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
    var httpContext = HttpContext.Current;
    if (httpContext == null)
    {
        base.HandleUnauthorizedRequest(actionContext);
        return;
    }

    actionContext.Response = httpContext.User.Identity.IsAuthenticated == false ?
        actionContext.Request.CreateErrorResponse(
      System.Net.HttpStatusCode.Unauthorized, "Unauthorized") :
       actionContext.Request.CreateErrorResponse(
      System.Net.HttpStatusCode.Forbidden, "Forbidden");

    httpContext.Response.SuppressFormsAuthenticationRedirect = true;
    httpContext.Response.End();
}
Zapalony programista
źródło
0

Dzięki chłopaki!

W moim przypadku połączyłem odpowiedzi cuongle i Shivy i otrzymałem coś takiego:

W module obsługi OnException () dla wyjątków API:

filterContext.ExceptionHandled = true;
//...
var response = filterContext.HttpContext.Response;
response.Headers.Add("Suppress-Redirect", "true");
response.SuppressFormsAuthenticationRedirect = true;

W kodzie konfiguracji uruchamiania aplikacji:

app.UseCookieAuthentication(new CookieAuthenticationOptions {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString("/Account/Login"),
        Provider = new CookieAuthenticationProvider {
            OnValidateIdentity = ctx => {
                return validateFn.Invoke(ctx);
            },
            OnApplyRedirect = ctx =>
            {
                bool enableRedir = true;
                if (ctx.Response != null)
                {
                    string respType = ctx.Response.ContentType;
                    string suppress = ctx.Response.Headers["Suppress-Redirect"];
                    if (respType != null)
                    {
                        Regex rx = new Regex("^application\\/json(;(.*))?$",
                            RegexOptions.IgnoreCase);
                        if (rx.IsMatch(respType))
                        {
                            enableRedir = false;
                        }  
                    }
                    if ((!String.IsNullOrEmpty(suppress)) && (Boolean.Parse(suppress)))
                    {
                        enableRedir = false;
                    }
                }
                if (enableRedir)
                {
                    ctx.Response.Redirect(ctx.RedirectUri);
                }
            }
        }
    });
Chakrit W
źródło
-1

W MVC 5 z Dot Net Framework 4.5.2 otrzymujemy „application / json, plaint text ..” pod nagłówkiem „Accept” Przyjemnie będzie użyć następującego:

isJson = headers["Content-Type"] == "application/json" || headers["Accept"].IndexOf("application/json", System.StringComparison.CurrentCultureIgnoreCase) >= 0;
Imran Javed
źródło