2012-10-19 29 views
32

Oto scenariusz:Droga prawidłowo obsługi HttpAntiForgeryException w MVC 4 Zastosowanie

Mam stronę logowania, gdy użytkownik podpisać to jest przekierowywany do strony aplikacji w domu. Następnie użytkownik korzysta z przycisku wstecz przeglądarki, a teraz jest na stronie logowania. Próbuje zalogować się ponownie, ale teraz jest wyjątek:

HttpAntiForgeryException (0x80004005): Podany anty-fałszowanie żeton była przeznaczona dla użytkownika „”, ale bieżący użytkownik jest „username”.

Wiem, że jest to związane z buforowaniem. I wyłączone buforowanie przeglądarka działania logowania przy użyciu niestandardowych nocache filtr, który określa wszystkie wymagane nagłówki - no-cache, no-store, trzeba-revalidate, etc. Ale

  • ten nie działa na wszystkich przeglądarkach
  • zwłaszcza Safari (mobile w większości przypadków) totalnie ignoruje te ustawienia

postaram się zrobić hacki i zmusić safari komórkę do odświeżenia, ale to nie jest to, czego oczekuję.

Chciałabym wiedzieć, czy mogę:

  • wyjątek uchwyt bez pokazywania użytkownikowi jakikolwiek problem istnieje (całkowicie przezroczysty dla użytkownika)
  • uniknąć tego problemu poprzez zastąpienie anty oszustwie nazwę tokena użytkownika, które będą pozwól użytkownikowi na ponowne zalogowanie się bez tego wyjątku, jeśli moje hacki związane z buforowaniem przeglądarki przestaną działać w następnych wersjach przeglądarek.
  • Naprawdę nie chciałbym polegać na zachowaniu przeglądarki, ponieważ każdy zachowuje się inaczej.

UPDATE 1

wprowadzić pewne wyjaśnienia, wiem, jak obsługiwać błędy w MVC. Problem polega na tym, że te błędy obsługi wcale nie rozwiązują mojego problemu. Podstawową ideą obsługi błędów jest przekierowanie do niestandardowej strony błędu z ładną wiadomością. Ale chcę zapobiec wystąpieniu tego błędu, aby nie obsługiwać go w sposób widoczny dla użytkownika. Przez uchwyt rozumiem catch nazwa_użytkownika zastąp lub inne odpowiednie działanie, a następnie kontynuuj logowanie.

UPDATE 2

Dodałem poniżej rozwiązania, które pracuje dla mnie.

Odpowiedz

16

Po pewnym czasie dochodzenia, wydaje mi się, że znalazłem sposób na pozbycie się tego błędu dla użytkownika. To nie jest idealne, ale przynajmniej nie wyświetlać stronę błędu:

stworzyłem filtr na podstawie HandleErrorAttribute:

[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", 
     Justification = "This attribute is AllowMultiple = true and users might want to override behavior.")] 
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] 
    public class LoginAntiforgeryHandleErrorAttribute : FilterAttribute, IExceptionFilter 
    { 
     #region Implemented Interfaces 

     #region IExceptionFilter 

     /// <summary> 
     /// </summary> 
     /// <param name="filterContext"> 
     /// The filter context. 
     /// </param> 
     /// <exception cref="ArgumentNullException"> 
     /// </exception> 
     public virtual void OnException(ExceptionContext filterContext) 
     { 
      if (filterContext == null) 
      { 
       throw new ArgumentNullException("filterContext"); 
      } 

      if (filterContext.IsChildAction) 
      { 
       return; 
      } 

      // If custom errors are disabled, we need to let the normal ASP.NET exception handler 
      // execute so that the user can see useful debugging information. 
      if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled) 
      { 
       return; 
      } 

      Exception exception = filterContext.Exception; 

      // If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method), 
      // ignore it. 
      if (new HttpException(null, exception).GetHttpCode() != 500) 
      { 
       return; 
      } 

      // check if antiforgery 
      if (!(exception is HttpAntiForgeryException)) 
      { 
       return; 
      } 

      filterContext.Result = new RedirectToRouteResult(
       new RouteValueDictionary 
       { 
        { "action", "Index" }, 
        { "controller", "Home" } 
       }); 

      filterContext.ExceptionHandled = true; 
     } 

     #endregion 

     #endregion 
    } 

Potem zastosowano ten filtr do logowania POST czynność:

[HttpPost] 
[AllowAnonymous] 
[ValidateAntiForgeryToken] 
[LoginAntiforgeryHandleError] 
public ActionResult Login(Login model, string returnUrl) 
{ 

Główną ideą tego rozwiązania jest przekierowanie wyjątku zapobiegającego fałszerstwu do głównego działania indeksu. Jeśli użytkownik nadal nie będzie nieuwierzytelniony, wyświetli stronę: zaloguj się, jeśli użytkownik będzie już uwierzytelniony, pokaże się strona indeksu.

UPDATE 1 Jest jeden potencjalny problem z tym rozwiązaniem. Jeśli ktoś loguje się z różnymi danymi uwierzytelniającymi, wówczas w przypadku błędu należy dodać dodatkowe środowisko wykonawcze logowania - wylogować poprzedniego użytkownika i zalogować się na nowy. Ten scenariusz nie jest obsługiwany.

+0

to działało świetnie dla mnie. Nie potrzebuję tego do niczego. Po prostu nie rzucaj żółtym ekranem. – hal9000

5

Powinieneś być w stanie obsłużyć wyjątek, dodając filtr akcji, aby poradzić sobie z błędem.

[HandleError(View="AntiForgeryExceptionView", ExceptionType = typeof(HttpAntiForgeryException))] 

Todo, więc upewnij się, że niestandardowe błędy są włączone w pliku web.config.

<customErrors mode="On"/> 

Można również spojrzeć na ten blog uzyskać więcej informacji na temat błędu uchwytu.

Edit Ponieważ używasz MVC4 a blog jest o MVC3 można również spojrzeć na MSDN library - HandleErrorAttribute, ale wersja nie powinien naprawdę zrobić różnicę.

+0

tak, ale to wszystko mówi o redirectling (tak obchodzenia się z błędów przerywa bieżące działania (wykonania) Chciałbym obsłużyć ten błąd bez przerywania. akcja, więc w tym przypadku uchwyt, zamień nazwę użytkownika i kontynuuj logowanie z właściwymi danymi – Marcin

+0

Myślę, że kiedy n złap błąd, możesz przekierować go do widoku (kontrolera), gdzie zastąpisz nazwę użytkownika i kontynuować logowanie, z wcześniej wprowadzonymi danymi. Zobacz także: http://stackoverflow.com/questions/1794936/how-do-i-pass-viewdata-to-a-handleerror-view –

15

Jeśli problem dotyczy tylko jednej lub kilku funkcji, utworzenie filtra może być nieco przesadą techniczną. Prostszy ale non generic rozwiązaniem jest po prostu usunąć [ValidateAntiForgeryToken] dla konkretnej metody i dodać ręcznego sprawdzania poprawności po sprawdzeniu, czy użytkownik jest zalogowany

if (User.Identity.IsAuthenticated) 
{ 
    return RedirectToAction("Index", "Home"); 
} 
System.Web.Helpers.AntiForgery.Validate(); 
/* proceed with authentication here */ 
+0

Dzięki, wydaje się, że działa świetnie i nadal zapewnia to samo bezpieczeństwo. –

+0

to nie działa ze mną, daje dokładnie ten sam błąd, ale w tym wierszu: System.Web.Helpers.AntiForgery.Validate(); –

1

Stary pytanie. - ale wpadłem na ten problem dzisiaj, i sposób w jaki rozwiązano go było przekierowanie do działania wylogowania tak:

public ActionResult Login(string returnUrl) 
{ 
    if (WebSecurity.IsAuthenticated) 
     return RedirectToAction("LogOff"); 

    ... 
}