2011-05-10 15 views
13

Ésta es mi Global.asax.cs archivo:Cuando `PostAuthenticateRequest` se ejecuta?

public class MvcApplication : System.Web.HttpApplication 
{ 
    public static void RegisterRoutes(RouteCollection routes) 
    { 
     ... 
    } 

    protected void Application_Start() 
    { 
     this.PostAuthenticateRequest += new EventHandler(MvcApplication_PostAuthenticateRequest); 
    } 

    // This method never called by requests... 
    protected void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e) 
    { 
     HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName]; 

     if (authCookie != null) 
     { 
      FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value); 
      var identity = new GenericIdentity(authTicket.Name, "Forms"); 
      var principal = new GenericPrincipal(identity, new string[] { }); 
      Context.User = principal; 
     } 
    } 
} 

Cuando PostAuthenticateRequest consigue ejecutar?

Respuesta

20

De acuerdo con la documentation:

Se produce cuando un módulo de seguridad tiene estableció la identidad del usuario.

...

El evento es PostAuthenticateRequest levantado después de ocurrido el evento AuthenticateRequest. La funcionalidad que suscribe al evento PostAuthenticateRequest puede acceder a los datos que se procesa mediante PostAuthenticateRequest.

y aquí está la ASP.NET Page Life Cycle.

Pero como su pregunta está etiquetada con ASP.NET MVC, le recomiendo que realice esto en un atributo personalizado [Authorize] en lugar de utilizar este evento. Ejemplo:

public class MyAuthorizeAttribute : AuthorizeAttribute 
{ 
    protected override bool AuthorizeCore(HttpContextBase httpContext) 
    { 
     var isAuthorized = base.AuthorizeCore(httpContext); 
     if (isAuthorized) 
     { 
      var authCookie = httpContext.Request.Cookies[FormsAuthentication.FormsCookieName]; 
      if (authCookie != null) 
      { 
       var authTicket = FormsAuthentication.Decrypt(authCookie.Value); 
       var identity = new GenericIdentity(authTicket.Name, "Forms"); 
       var principal = new GenericPrincipal(identity, new string[] { }); 
       httpContext.User = principal; 
      } 
     } 
     return isAuthorized; 
    } 
} 

Ahora decorar sus controladores/acciones con el atributo [MyAuthorize]:

[MyAuthorize] 
public ActionResult Foo() 
{ 
    // if you got here the User property will be the custom 
    // principal you injected in the authorize attribute 
    ... 
} 
+4

¿Por qué recomienda aplicar filtros en todos los controladores, cuando parece mucho más limpio tener el cambio en un solo lugar en el controlador de eventos? ¿Cuál es la ganancia? –

+1

Para este ejemplo, probablemente tendría más sentido reemplazar var identity = new GenericIdentity (authTicket.Name, "Forms"); con var identity = new FormsIdentity (authTicket); –

+6

@zespri El evento PostAuthenticateRequest se puede llamar varias veces por página. El uso de un atributo de autorización personalizado garantiza que su código solo se llame una vez por solicitud. – Mark

9

Si coloca el código en PostAuthenticateRequest que puede ser golpeado muchas veces por solicitud como todos los recursos tales como imágenes y estilo las hojas a las que se hace referencia en su página activarán este evento ya que se tratan como solicitudes separadas.

Si elige la respuesta de @ Darin, AuthorizeAttribute no procesará la acción cuando isAuthorized devuelva false, pero las personas pueden necesitar que se represente de todos modos, incluso si es una página pública (acceso no restringido) que desee mostrar un "Nombre para mostrar" guardado en la parte userData del authTicket.

Para ello, recomiendo cargar el authCookie en una ActionFilterAttribute (AuthenticationFilter):

public class LoadCustomAuthTicket : ActionFilterAttribute, IAuthenticationFilter 
{ 
    public void OnAuthentication(AuthenticationContext filterContext) 
    { 
     if (!filterContext.Principal.Identity.IsAuthenticated) 
      return; 

     HttpCookie authCookie = filterContext.HttpContext.Request.Cookies[FormsAuthentication.FormsCookieName]; 

     if (authCookie == null) 
      return; 

     FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value); 
     var identity = new GenericIdentity(authTicket.Name, "Forms"); 
     var principal = new GenericPrincipal(identity, new string[] { }); 

     // Make sure the Principal's are in sync. see: https://www.hanselman.com/blog/SystemThreadingThreadCurrentPrincipalVsSystemWebHttpContextCurrentUserOrWhyFormsAuthenticationCanBeSubtle.aspx 
     filterContext.Principal = filterContext.HttpContext.User = System.Threading.Thread.CurrentPrincipal = principal; 

    } 
    public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext) 
    { 
     //This method is responsible for validating the current principal and permitting the execution of the current action/request. 
     //Here you should validate if the current principle is valid/permitted to invoke the current action. (However I would place this logic to an authorization filter) 
     //filterContext.Result = new RedirectToRouteResult("CustomErrorPage",null); 
    } 
} 

Y en global.asax.cs

public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
    { 
     filters.Add(new LoadCustomAuthTicket()); 
    } 

De esa manera también no tendrá que llenar todas tus acciones con el atributo.

+0

¿Esta acción filtraría los pasos de un atributo de autorización personalizado si se aplican ambos a una acción? –

+0

Además, ¿habría una forma de garantizar que este atributo se ejecute antes de un atributo de autorización? Idealmente, solo cargaríamos la cookie en un punto, antes de que se ejecute cualquier atributo. –

+0

Se encontró un orden de operación relevante e interesante. http://stackoverflow.com/questions/6561883/in-what-order-are-filters-executed-in-asp-net-mvc –

Cuestiones relacionadas