11

Pracuję nad projektem ASP.NET MVC5, który ma włączone uwierzytelnianie formularzy. Projekt znajduje się obecnie w fazie testowej i jest hostowany online na platformie Azure, ale właściciel projektu chciałby wyłączyć cały publiczny dostęp do witryny (ponieważ niektóre części witryny nie wymagają w ogóle uwierzytelniania użytkownika).Podstawowe uwierzytelnianie HTTP ASP.NET MVC5 i wyjątek AntiForgeryToken

W tej fazie testowej zdecydowaliśmy się wdrożyć podstawowe uwierzytelnianie HTTP, od tego link. Zmieniłem kod, więc lepiej pasuje do moich potrzeb:

public class BasicAuthenticationAttribute : FilterAttribute, IAuthorizationFilter 
{ 
    public string BasicRealm { get; set; } 

    protected string Username { get; set; } 
    protected string Password { get; set; } 

    public BasicAuthenticationAttribute(string username, string password) 
    { 
     this.Username = username; 
     this.Password = password; 
    } 

    public void OnAuthorization (AuthorizationContext filterContext) 
    { 
     var req = filterContext.HttpContext.Request; 
     var auth = req.Headers["Authorization"]; 
     if (!String.IsNullOrEmpty(auth)) 
     { 
      var cred = System.Text.ASCIIEncoding.ASCII.GetString(Convert.FromBase64String(auth.Substring(6))).Split(':'); 
      var user = new { Name = cred[0], Pass = cred[1] }; 

      if (user.Name == Username && user.Pass == Password) 
       return; 
     } 

     var res = filterContext.HttpContext.Response; 
     var alreadySent = HttpContext.Current.Items.Contains("headers-sent"); 

     if (!alreadySent) 
     { 
      res = filterContext.HttpContext.Response; 
      res.StatusCode = 401; 
      res.AppendHeader("WWW-Authenticate", String.Format("Basic realm=\"{0}\"", BasicRealm ?? "Test")); 

     } 
    } 
} 

Ja również zarejestrował go jako filtr globalnej:

public class FilterConfig 
{ 
    public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
    { 
     filters.Add(new HandleErrorExtendedAttribute()); 
     filters.Add(new BasicAuthenticationAttribute(AppConfig.BasicUsername, AppConfig.BasicPassword)); 
    } 
} 

Jednakże istnieją pewne problemy, kiedy uruchomić projekt . Jeśli używam tej wersji kodu:

 if (!alreadySent) 
     { 
      res = filterContext.HttpContext.Response; 
      res.StatusCode = 401; 
      res.AppendHeader("WWW-Authenticate", String.Format("Basic realm=\"{0}\"", BasicRealm ?? "Test")); 
     } 

po pomyślnym zalogowaniu się stale przekierowuje do strony logowania formularzy.

Jednak gdybym dołączyć

res.End(); 

po

res.AppendHeader("WWW-Authenticate", String.Format("Basic realm=\"{0}\"", BasicRealm ?? "Test")); 

Antiforgerytoken w plikach cshtml rzuca System.Web.HttpException

Server nie może dołączyć nagłówek po nagłówki HTTP zostały wysłane.

Ale w tym przypadku ostatecznie pomyślnie uwierzytelnia.

Obecnie utknąłem na tym i nie mam pojęcia, jak rozwiązać ten problem, ponieważ wyłączanie uwierzytelniania formularzy nie jest i opcja i nie mogę usunąć wszystkich AntiForgeryTokens i ich sprawdzania poprawności.

+2

Dlaczego wybraliście to podejście?Jeśli chcesz zbudować niestandardowy filtr uwierzytelniania, powinieneś dziedziczyć po 'AuthorizeAttribute', przesłonić funkcję' IsAuthorized', zwrócić 'false' dla nieautoryzowanych żądań i obsłużyć je w funkcji' HandleUnauthorizedRequest'. – AnhTriet

+0

Jeśli zmienisz wiersz res.StatusCode wierszem res.AppendHeader, co by się stało? Czy wartość StatusCode jest liczona jako część "Body" tak, że zamyka nagłówki od zapisania? Proszę również rozważyć odpowiedź Andrew poniżej. –

+0

plz nie używaj podstawowego uwierzytelnienia. Zauważ, że inne pytanie, do którego się odwołujesz, ma już 6 lat, i nawet wtedy były lepsze alternatywy. Zobacz: http://adrianotto.com/2013/02/why-http-basic-auth-is-bad/ – nothingisnecessary

Odpowiedz

1

Wypróbowałeś tę linię przed res.AppendHeader (..)?

Response.ClearHeaders(); 
3

Proponuję użyć dostawcę członkostwa ASP.NET Identity ponieważ to jest wliczone w MVC 5. Za pomocą tego można po prostu uwierzytelnianie użytkowników bez pisania dużo kodu, jak to było z poprzednim Członkostwa. Może również używać zewnętrznego logowania przy użyciu wewnętrznych plików cookie, tak jak w przypadku metody FormsAuthentication. Wszystko, czego potrzebujesz to proste konfiguracje w kodzie i nie musisz pisać własnych filtrów.

1

Zamiast kończenia żądania z req.End(), myślę, że chcesz ustawić filterContext.Result, jak pokazano poniżej. Ustawienie wyniku na wartość inną niż null przerwie przetwarzanie pozostałej części potoku MVC i spowoduje wysłanie odpowiedzi do przeglądarki, ale nie powinno to zapieczętować odpowiedzi jako End(), więc nie powinieneś otrzymywać wyjątku o nagłówkach już wysłanych.

Spróbuj tego:

filterContext.Result = new HttpUnauthorizedResult();