2012-10-19 17 views
32

Ecco lo scenario:Un modo di gestire correttamente HttpAntiForgeryException in MVC 4 applicazione

Ho una pagina di login, quando l'utente firmarlo è reindirizzato alla pagina dell'applicazione casa. Quindi l'utente utilizza il pulsante Indietro del browser e ora si trova nella pagina di accesso. Cerca di login di nuovo, ma ora viene generata un'eccezione:

HttpAntiForgeryException (0x80004005): Il token anti-contraffazione previsto era destinato per l'utente "", ma l'utente corrente è "username".

So che questo è correlato al caching. Ho disattivato il caching del browser per l'azione di login utilizzando il filtro NoCache personalizzato che imposta tutte le intestazioni necessarie - no-cache, no-store, must-rinnovo, ecc Ma

  • questo non funziona su tutti i browser
  • soprattutto Safari (nella maggior parte dei casi mobile) ignora totalmente tali impostazioni

Cercherò di fare in modo che gli hack e il force safari mobile si aggiornino, ma questo non è quello che mi aspetto.

Vorrei sapere se posso:

  • eccezione maniglia senza mostrare all'utente alcun problema esiste (totalmente trasparente per l'utente)
  • evitare questo problema sostituendo contro contraffazione token nome utente che sarà consentire nuovamente l'accesso utente senza questa eccezione, se i miei hack relativi alla memorizzazione nella cache del browser smetteranno di funzionare nelle prossime versioni dei browser.
  • Mi piacerebbe davvero non fare affidamento sul comportamento del browser, dal momento che ognuno si comporta in modo diverso.

UPDATE 1

di fare qualche precisazione, so come gestire gli errori in MVC. Il problema è che questo errore di gestione non risolve affatto il mio problema. L'idea di base della gestione degli errori è il reindirizzamento alla pagina di errore personalizzata con un bel messaggio. Ma voglio evitare che questo errore si verifichi, non gestirlo in modo visibile all'utente. Per maniglia intendo catch make nome utente sostitutivo o altra azione appropriata, quindi continuare il login.

UPDATE 2

ho aggiunto sotto soluzione che sta lavorando per me.

risposta

16

Dopo un po 'di tempo di indagine, penso di aver trovato un modo per sbarazzarsi di questo errore per l'utente. Non è perfetto, ma almeno non visualizzare la pagina di errore:

ho creato filtro in base 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 
    } 

Poi ho applicato questo filtro alla Login azione POST:

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

L'idea principale di questa soluzione è di reindirizzare l'eccezione anti-contraffazione all'azione principale dell'indice. Se l'utente non sarà ancora non autenticato, mostrerà la pagina di login se l'utente sarà già autenticato mostrerà la pagina indice.

UPDATE 1 C'è un potenziale problema con questa soluzione. Se qualcuno sta effettuando l'accesso con credenziali diverse, in caso di errore dovrebbe essere aggiunto un ulteriore runtime di accesso - disconnetti l'utente precedente e accedi con uno nuovo. Questo scenario non è gestito.

+0

questo ha funzionato alla grande per me. Non ne ho bisogno per fare fantasia. Basta non gettare lo schermo giallo. – hal9000

5

Dovresti essere in grado di gestire l'eccezione aggiungendo un filtro azione per gestire il tuo errore.

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

Todo quindi assicurati che gli errori personalizzati siano attivati ​​nel tuo web.config.

<customErrors mode="On"/> 

Si potrebbe anche dare un'occhiata a questo blog per maggiori informazioni su errori maniglia.

Modifica Dal momento che si sta utilizzando MVC4 e il blog è di MVC3 si potrebbe anche dare un'occhiata al MSDN library - HandleErrorAttribute, ma la versione non si dovrebbe davvero fare la differenza.

+0

sì, ma questo dice tutto redirectling (così la manipolazione di questo errori rompe azione corrente (esecuzione) Vorrei gestire questo errore senza rompersi. azione, quindi in questo caso gestire, sostituire il nome utente e continuare il login con i dati corretti – Marcin

+0

Penso che quando si ca n prendere l'errore è possibile reindirizzare a una vista (controller) in cui si sostituisce il nome utente e continuare il login, con i dati inseriti in precedenza. Vedi anche: http://stackoverflow.com/questions/1794936/how-do-i-pass-viewdata-to-a-handleerror-view –

15

Se si dispone solo di una o di alcune funzioni interessate, la creazione di un filtro potrebbe essere un eccesso tecnico eccessivo. Una soluzione più semplice, ma non generica è quella di rimuovere semplicemente il [ValidateAntiForgeryToken] per il metodo specifico e aggiungere una convalida manuale dopo aver verificato se l'utente è connesso

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

Grazie, sembra funzionare alla grande e offre ancora la stessa sicurezza. –

+0

questo non ha funzionato con me, dà esattamente lo stesso errore ma su questa linea: System.Web.Helpers.AntiForgery.Validate(); –

1

una vecchia questione -. Ma mi sono imbattuto in questo problema oggi, e il modo in cui ho risolto era reindirizzando all'azione disconnessione in questo modo:

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

    ... 
} 
Problemi correlati