2010-04-05 15 views
22

¿Cómo se manejan las solicitudes ajax cuando el usuario no está autenticado?¿Cómo se manejan las solicitudes ajax cuando el usuario no está autenticado?

Alguien entra a la página, deja espacio durante una hora, regresa, agrega un comentario en la página que recorre ajax usando jQuery ($.post). Como no está autenticado, el método devuelve el resultado de RedirectToRoute (redirige a la página de inicio de sesión). Qué haces con eso? ¿Cómo se maneja en el lado del cliente y cómo se maneja en el controlador?

+0

¿Su sesión se agotó después de 20 minutos de inactividad? ¿Tu aplicación redirige a otra página? – Raja

+0

@Raja: el estado de inicio de sesión se agota después de un tiempo y estoy preguntando cómo manejar esta situación :) Sí, redirige a la página de inicio de sesión. – LukLed

Respuesta

16

EDIT:

he escrito anteriormente respuesta hace mucho tiempo y ahora creo que el envío de 403 no es forma correcta de ir. 403 tiene un significado ligeramente diferente y simplemente no debe usarse. Esto se corrige atributo mediante 401. Sólo difiere con context.HttpContext.Response.End() adicional en Http401Result y diferente código HTTP:

public class OptionalAuthorizeAttribute : AuthorizeAttribute 
{ 
    private class Http401Result : ActionResult 
    { 
     public override void ExecuteResult(ControllerContext context) 
     { 
      // Set the response code to 401. 
      context.HttpContext.Response.StatusCode = 401; 
      context.HttpContext.Response.Write(CTRes.AuthorizationLostPleaseLogOutAndLogInAgainToContinue); 
      context.HttpContext.Response.End(); 
     } 
    } 

    private readonly bool _authorize; 

    public OptionalAuthorizeAttribute() 
    { 
     _authorize = true; 
    } 

    //OptionalAuthorize is turned on on base controller class, so it has to be turned off on some controller. 
    //That is why parameter is introduced. 
    public OptionalAuthorizeAttribute(bool authorize) 
    { 
     _authorize = authorize; 
    } 

    protected override bool AuthorizeCore(HttpContextBase httpContext) 
    { 
     //When authorize parameter is set to false, not authorization should be performed. 
     if (!_authorize) 
      return true; 

     var result = base.AuthorizeCore(httpContext); 

     return result; 
    } 

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest()) 
     { 
      //Ajax request doesn't return to login page, it just returns 401 error. 
      filterContext.Result = new Http401Result(); 
     } 
     else 
      base.HandleUnauthorizedRequest(filterContext); 
    } 
} 

vieja respuesta:

Aunque me gusta las ideas publicadas en otras respuestas (que tenía una idea sobre antes), necesitaba ejemplos de código. Aquí están:

Modificado atributo Autorizar:

public class OptionalAuthorizeAttribute : AuthorizeAttribute 
{ 
    private class Http403Result : ActionResult 
    { 
     public override void ExecuteResult(ControllerContext context) 
     { 
      // Set the response code to 403. 
      context.HttpContext.Response.StatusCode = 403; 
      context.HttpContext.Response.Write(CTRes.AuthorizationLostPleaseLogOutAndLogInAgainToContinue); 
     } 
    } 

    private readonly bool _authorize; 

    public OptionalAuthorizeAttribute() 
    { 
     _authorize = true; 
    } 

    //OptionalAuthorize is turned on on base controller class, so it has to be turned off on some controller. 
    //That is why parameter is introduced. 
    public OptionalAuthorizeAttribute(bool authorize) 
    { 
     _authorize = authorize; 
    } 

    protected override bool AuthorizeCore(HttpContextBase httpContext) 
    { 
     //When authorize parameter is set to false, not authorization should be performed. 
     if (!_authorize) 
      return true; 

     var result = base.AuthorizeCore(httpContext); 

     return result; 
    } 

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest()) 
     { 
      //Ajax request doesn't return to login page, it just returns 403 error. 
      filterContext.Result = new Http403Result(); 
     } 
     else 
      base.HandleUnauthorizedRequest(filterContext); 
    } 
} 

HandleUnauthorizedRequest se anula por lo que vuelve Http403Result cuando se utiliza Ajax. Http403Result cambia StatusCode a 403 y devuelve un mensaje al usuario en respuesta. Hay alguna lógica adicional en el atributo (parámetro authorize), porque enciendo [Authorize] en el controlador base y lo deshabilito en algunas páginas.

La otra parte importante es el manejo global de esta respuesta en el lado del cliente. Esto es lo que he puesto en Site.Master:

<script type="text/javascript"> 
    $(document).ready(
     function() { 
      $("body").ajaxError(
       function(e,request) { 
        if (request.status == 403) { 
         alert(request.responseText); 
         window.location = '/Logout'; 
        } 
       } 
      ); 
     } 
    ); 
</script> 

me coloque un controlador de error de Ajax global y cuando cada vez $.post falla con un error 403, el mensaje de respuesta es alertado y se redirige al usuario a cerrar la sesión página. Ahora no tengo que manejar el error en cada solicitud $.post, ya que se maneja de forma global.

¿Por qué 403, y no 401? 401 es manejado internamente por MVC framework (es por eso que la redirección a la página de inicio de sesión se realiza después de la autorización fallida).

¿Qué opina sobre eso?

+0

Es curioso que Microsoft no haya proporcionado un controlador predeterminado como este para las solicitudes ajax. – Cherian

+0

@Cherian: Microsoft promociona fuertemente jQuery con ASP.NET, por lo que no es tan extraño. Microsoft AJAX todavía está aquí, porque muchas personas se acostumbraron con Webforms de ASP.NET, pero creo que jQuery es la forma preferida. – LukLed

+0

No quise decir eso. jQuery todavía es el camino. Quise decir por qué no tenían un "AjaxAuthorize (return =" json ")" que anula su manejo 401 y nos da probablemente {success: "false", status = 401} – Cherian

4

La idea que se me ocurrió cuando un compañero de trabajo me preguntó cómo manejarla fue la siguiente: crear un atributo AuthorizeAjax. Puede interrogar y verificar que Request.IsAjaxRequest() y, si la solicitud no está autenticada, devolver un objeto de error JSON específico. Es posible que simplemente anule el atributo AuthorizeAttribute predeterminado y haga que llame a la base a menos que se trate de una solicitud AJAX no autorizada, por lo que no debe preocuparse por etiquetar las acciones del controlador con [Authorize] o [AuthorizeAjax].

En el lado del cliente, todas sus páginas deberían estar equipadas para tratar el error devuelto, pero esa lógica probablemente pueda compartirse.

+0

¿Cómo lo compartes? ¿Cómo se ve tu atributo AuthorizeAjax? – LukLed

+0

No puedo compartir eso, pero ASP.Net MVC es de código abierto, por lo que puede ver cómo lo implementaron para obtener ideas. En lo que respecta a la compartición del lado del cliente, puede tratar vinculando una función central (un diálogo o alerta o lo que sea) al evento de error AJAX (http://docs.jquery.com/Ajax_Events). – 48klocs

2

Propongo crear su propio AuthorizeAttribute y si la solicitud es una solicitud de Ajax, ejecute HttpException (401/403). Y también cambie para usar jQuery's Ajax Method en su lugar.

Suponiendo que ha implementado páginas de error y devuelven el código de estado correcto, se ejecutará la devolución de llamada error en lugar de la devolución de llamada success. Esto sucederá debido al código de respuesta.

0

La solución más sencilla y limpia que he encontrado para esto es registrar una devolución de llamada con el evento jQuery.ajaxSuccess() y verificar el encabezado de respuesta "X-AspNetMvc-Version".

Cada petición jQuery Ajax en mi aplicación es manejado por MVC así que si la cabecera no se encuentra sé que mi solicitud ha sido redirigido a la página de inicio de sesión, y simplemente volver a cargar la página para una redirección de nivel superior:

$(document).ajaxSuccess(function(event, XMLHttpRequest, ajaxOptions) { 
    // if request returns non MVC page reload because this means the user 
    // session has expired 
    var mvcHeaderName = "X-AspNetMvc-Version"; 
    var mvcHeaderValue = XMLHttpRequest.getResponseHeader(mvcHeaderName); 

    if (!mvcHeaderValue) { 
     location.reload(); 
    } 
}); 

La recarga de la página puede causar algunos errores de Javascript (dependiendo de lo que esté haciendo con la respuesta de Ajax) pero en la mayoría de los casos donde la depuración está desactivada, el usuario nunca los verá.

Si no desea utilizar el encabezado integrado, estoy seguro de que puede agregar fácilmente uno personalizado y seguir el mismo patrón.

0

Aquí hay una solución que uso. Es muy simple, si un poco de fuerza bruta. Me gusta porque soy flojo y no quiero pensar en atributos especiales en los métodos de acción y no quiero escribir controladores de errores ajax si no tengo que hacerlo (aunque no hay razón para que el script del cliente no pueda detectar el código de estado 403 y hacer algo fácil de usar).

Poniendo esto en Global.axax detecta cualquier solicitud ajax no autenticada y simplemente devuelve 403, sin contenido. Esto evita que las llamadas ajax no autenticadas sean redirigidas al formulario de inicio de sesión cuando la autenticación de formularios está en uso.

protected void Application_AuthenticateRequest(object sender, EventArgs e) 
    { 
     // Prevent Ajax requests from being returned the login form when not authenticated 
     // (eg. after authentication timeout). 
     if ((Request.Headers["X-Requested-With"] != null && Request.Headers["X-Requested-With"] == "XMLHttpRequest") 
      || 
      (Request["X-Requested-With"] != null && Request["X-Requested-With"] == "XMLHttpRequest")) 
     { 
      if (!Request.IsAuthenticated) 
      { 
       Response.Clear(); 
       Response.StatusCode = 403; 
       Response.Flush(); 
       Response.End(); 
      } 
     } 
    } 
0

Puede detectar petición AJAX y enviar 401, y en el lado del cliente incluso se puede mostrar un cuadro de diálogo ajax con indicador de entrada, después de lo cual se puede "seguir" su petición ajax fallado y hacer su trabajo de aplicación y la sensación de usuario como el tiempo de espera de la sesión nunca sucedió. Ver this answer para más detalles.

Cuestiones relacionadas