2008-09-23 26 views
35

Cómo deshabilitar el manejo estándar de ASP.NET del código de respuesta 401 (redirigir a la página de inicio de sesión) para solicitudes AJAX/JSON?401 código de respuesta para solicitudes json con ASP.NET MVC

Para páginas web está bien, pero para AJAX tengo que obtener el código de error 401 correcto en lugar de 302/200 para la página de inicio de sesión.

actualización: Hay varias soluciones de Phil Haack, PM de ASP.NET MVC - http://haacked.com/archive/2011/10/04/prevent-forms-authentication-login-page-redirect-when-you-donrsquot-want.aspx

Respuesta

18

el tiempo de ejecución de ASP.NET se desarrolla de modo que siempre se redirigirá al usuario si la HttpResponse.StatusCode se establece en 401, pero sólo si se encuentra la sección de <authentication /> del Web.config.

Extracción de la sección de autenticación será necesario que implementar la redirección a la página de inicio de sesión en su atributo, pero esto no debería ser un gran problema.

+0

La mejor manera de implementar su propia redirección es subclase AuthorizeAttribute, y cuando no esté autorizado, establezca el resultado en un RedirectResult en lugar de HttpUnauthorizedResult. –

+2

Sí, pero en este caso al OP le gustaría enviar el código HTTP 401, pero no redirigir (para que funcione correctamente con JSON). –

+0

Funciona para mí, aunque configuré la configuración como en lugar de eliminar el atributo por completo. –

1

Se podía elegir para crear una costumbre FilterAttribute implementando la interfaz IAuthorizationFilter.

En este atributo agrega lógica para determinar si se supone que la solicitud debe devolver JSON. De ser así, puede devolver un resultado JSON vacío (o hacer lo que quiera) dado que el usuario no ha iniciado sesión. Para otras respuestas, simplemente redirigiría al usuario como siempre.

Aún mejor, podría simplemente anular la OnAuthorization de la clase AuthorizeAttribute para que no tenga que reinventar la rueda. Añadir la lógica que he mencionado anteriormente y la intersección si el filterContext.Cancel es cierto (la filterContext.Result se establecerá en una instancia de la clase HttpUnauthorizedResult.

Leer más sobre "Filters in ASP.NET MVC CodePlex Preview 4" en el blog Phil Haacks. También se aplica a la última vista previa.

+0

Pero todavía no puedo obtener el código de estado de la respuesta 401 para la solicitud de AJAX. Incluso si configuro la respuesta en el filtro de Autorización. Dado que la infraestructura ASP.NET captura la respuesta 401 y la redirección a la página de inicio de sesión ocupa ASP.NET MVC. Dejar que sepan que el usuario no está conectado por el resultado JSON vacío no es muy correcto ... – derigel

+0

No entendí exactamente lo que tenía en mente hasta ahora. Estoy agregando una nueva respuesta que resolverá su problema. –

+1

¿cómo lograste publicar dos respuestas? :) – DevDave

2

También es posible usar el Global.asax para interrumpir este proceso con algo como esto:

protected void Application_PreSendRequestHeaders(object sender, EventArgs e) { 
     if (Response.StatusCode == 401) { 
      Response.Clear(); 
      Response.Redirect(Response.ApplyAppPathModifier("~/Login.aspx")); 
      return; 
     } 
    } 
+1

Lo hice, excepto que revisé el 302 y X-Requerido-Con en la respuesta de @troethom anterior. – Marc

24

En ASP.NET clásico se obtiene un código de respuesta HTTP 401 cuando se llama a un WebMethod con el Ajax. Espero que lo cambien en futuras versiones de ASP.NET MVC. En este momento estoy usando este truco:

protected void Application_EndRequest() 
{ 
    if (Context.Response.StatusCode == 302 && Context.Request.Headers["X-Requested-With"] == "XMLHttpRequest") 
    { 
     Context.Response.Clear(); 
     Context.Response.StatusCode = 401; 
    } 
} 
+0

Esta es una muy buena solución de paso si necesita algo rápido que funcione. De lo contrario, trataría de implementar @troethom answer. –

8

quería tanto la autenticación de formularios y para devolver un 401 para peticiones Ajax que no fueron autenticados.

Al final, creé un CustomizeAttribute personalizado y decoré los métodos del controlador. (Esto es en .Net 4.5)

//web.config

<authentication mode="Forms"> 
</authentication> 

// controlador

[Authorize(Roles = "Administrator,User"), Response302to401] 
[AcceptVerbs("Get")] 
public async Task<JsonResult> GetDocuments() 
{ 
    string requestUri = User.Identity.Name.ToLower() + "/document"; 
    RequestKeyHttpClient<IEnumerable<DocumentModel>, string> client = 
     new RequestKeyHttpClient<IEnumerable<DocumentModel>, string>(requestUri); 

    var documents = await client.GetManyAsync<IEnumerable<DocumentModel>>(); 

    return Json(documents, JsonRequestBehavior.AllowGet); 
} 

// AuthorizeAttribute

public class Response302to401 : AuthorizeAttribute 
{ 
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     if (!filterContext.HttpContext.User.Identity.IsAuthenticated) 
     { 
      if (filterContext.HttpContext.Request.IsAjaxRequest()) 
      { 
       filterContext.Result = new JsonResult 
       { 
        Data = new { Message = "Your session has died a terrible and gruesome death" }, 
        JsonRequestBehavior = JsonRequestBehavior.AllowGet 
       }; 
       filterContext.HttpContext.Response.StatusCode = 401; 
       filterContext.HttpContext.Response.StatusDescription = "Humans and robots must authenticate"; 
       filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true; 
      } 
     } 
     //base.HandleUnauthorizedRequest(filterContext); 
    } 
} 
+0

¿Qué sucede si deseo usar formularios estándar para autorizar redirecciones cuando un usuario no está conectado, pero también para invocar redireccionamientos 401 personalizados cuando un usuario no tiene los permisos pertinentes, alguna idea? – DevDave

+0

Obtendrá redirecciones de formularios normales en métodos que no tienen este AuthorizeAttribute. También podría expandir este atributo proporcionando la cláusula "else" de if (! FilterContext.IsAuthenticated) en la que verificaría los permisos y luego configuraría el redireccionamiento usted mismo. –

2

no veo lo tenemos que modificar el modo de autenticación o la etiqueta de autenticación como dice la respuesta actual.

Siguiendo la idea de @TimothyLeeRussell (gracias por cierto), he creado un atributo Autorizar personalizado (el problema con el de @TimothyLeeRussell es que se lanza una excepción porque intenta cambiar el filtroContext.Result an que genera una HttpException, y eliminar esa parte, además de filterContext.HttpContext.Response.StatusCode = 401, el código de respuesta siempre fue 200 OK). Así que finalmente resolví el problema finalizando la respuesta después de los cambios.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 
public class BetterAuthorize : AuthorizeAttribute 
{ 
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     if (filterContext.HttpContext.Request.IsAjaxRequest()) 
     { 
      //Set the response status code to 500 
      filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; 
      filterContext.HttpContext.Response.StatusDescription = "Humans and robots must authenticate"; 
      filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true; 

      filterContext.HttpContext.Response.End(); 
     } 
     else 
      base.HandleUnauthorizedRequest(filterContext); 
    } 
} 
+1

la propiedad SuppressFormsAuthenticationRedirect era exactamente lo que estaba buscando. – zeocrash

1

Puede llamar a este método dentro de su acción,

HttpContext.Response.End(); 

Ejemplo

public async Task<JsonResult> Return401() 
{ 
    HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; 
    HttpContext.Response.End(); 
    return Json("Unauthorized", JsonRequestBehavior.AllowGet); 
} 

De MSDN: El fin del método hace que el servidor Web para detener el procesamiento de la secuencia de comandos y devolver la corriente resultado. El contenido restante del archivo no se procesa.

Cuestiones relacionadas