¿Por qué AuthorizeAttribute redirige a la página de inicio de sesión para detectar errores de autenticación y autorización?


En ASP.NET MVC, puede marcar un método de controlador con AuthorizeAttribute, así:

[Authorize(Roles = "CanDeleteTags")]
public void Delete(string tagName)
{
    // ...
}

Esto significa que, si el usuario actualmente conectado no está en el rol "CanDeleteTags", nunca se llamará al método controlador.

Desafortunadamente, para fallas, AuthorizeAttribute devuelve HttpUnauthorizedResult, que siempre devuelve el código de estado HTTP 401. Esto provoca una redirección a la página de inicio de sesión.

Si el usuario no ha iniciado sesión, esto tiene mucho sentido. Sin embargo, si el usuario ya está registrado en, pero no está en el rol requerido, es confuso enviarlos de vuelta a la página de inicio de sesión.

Parece que AuthorizeAttribute combina autenticación y autorización.

Esto parece un poco un descuido en ASP.NET MVC, o me estoy perdiendo algo?

He tenido que cocinar un DemandRoleAttribute que separa los dos. Cuando el usuario no está autenticado, devuelve HTTP 401, enviándolo a la página de inicio de sesión. Cuando el usuario está conectado, pero no está en el rol requerido, crea un NotAuthorizedResult en su lugar. Actualmente esto redirige a una página de error.

Seguramente no tuve que hacer esto?

Author: Roger Lipscombe, 2008-10-26

6 answers

Cuando se desarrolló por primera vez, System.Web.Mvc.AuthorizeAttribute estaba haciendo lo correcto - las revisiones anteriores de la especificación HTTP usaban el código de estado 401 tanto para "no autorizado"como para " no autenticado".

De la especificación original:

Si la solicitud ya incluía credenciales de autorización, entonces la respuesta 401 indica que la autorización ha sido rechazada para esas credenciales.

De hecho, se puede ver la confusión allí-utiliza la palabra "autorización" cuando significa "autenticación". En la práctica cotidiana, sin embargo, tiene más sentido devolver un 403 Forbidden cuando el usuario está autenticado pero no autorizado. Es poco probable que el usuario tenga un segundo conjunto de credenciales que le den acceso - mala experiencia de usuario por todas partes.

Considere la mayoría de los sistemas operativos: cuando intenta leer un archivo al que no tiene permiso para acceder, ¡no se le muestra una pantalla de inicio de sesión!

Afortunadamente, el HTTP las especificaciones se actualizaron (junio de 2014) para eliminar la ambigüedad.

De "Hyper Text Transport Protocol (HTTP/1.1): Authentication" (RFC 7235):

El código de estado 401 (No autorizado) indica que la solicitud no se ha aplicado porque carece de credenciales de autenticación válidas para el recurso de destino.

De "Protocolo de Transferencia de Hipertexto (HTTP/1.1): Semántica y Contenido" (RFC 7231):

El código de estado 403 (Prohibido) indica que el el servidor entendió la solicitud pero se niega a autorizarla.

Curiosamente, en el momento ASP.NET MVC 1 fue liberado el comportamiento de AuthorizeAttribute era correcto. Ahora, el comportamiento es incorrecto: la especificación HTTP/1.1 fue corregida.

En lugar de intentar cambiar ASP.NET ' s página de inicio de sesión redirige, es más fácil solo para solucionar el problema en la fuente. Puede crear un nuevo atributo con el mismo nombre (AuthorizeAttribute) en el espacio de nombres predeterminado de su sitio web (esto es muy importante) entonces el compilador lo recogerá automáticamente en lugar del estándar de MVC. Por supuesto, siempre podría darle un nuevo nombre al atributo si prefiere tomar ese enfoque.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAuthenticated)
        {
            filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
        }
        else
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
    }
}
 288
Author: ShadowChaser,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-20 18:10:23

Agregue esto a su función de Login Page_Load:

// User was redirected here because of authorization section
if (User.Identity != null && User.Identity.IsAuthenticated)
    Response.Redirect("Unauthorized.aspx");

Cuando el usuario es redirigido allí pero ya ha iniciado sesión, muestra la página no autorizada. Si no están conectados, se cae y muestra la página de inicio de sesión.

 23
Author: Alan Jackson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-01 13:29:29

Siempre pensé que esto tenía sentido. Si ha iniciado sesión e intenta acceder a una página que requiere un rol que no tiene, se le reenvía a la pantalla de inicio de sesión pidiéndole que inicie sesión con un usuario que sí tiene el rol.

Puede agregar lógica a la página de inicio de sesión que verifique si el usuario ya está autenticado. Podría agregar un mensaje amistoso que explique por qué han sido golpeados allí de nuevo.

 4
Author: Rob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-27 16:28:46

Desafortunadamente, usted está tratando con el comportamiento predeterminado de ASP.NET autenticación de formularios. Hay una solución alternativa (no la he probado) discutida aquí:

Http://www.codeproject.com/KB/aspnet/Custon401Page.aspx

(No es específico de MVC)

Creo que en la mayoría de los casos la mejor solución es restringir el acceso a recursos no autorizados antes de que el usuario intente llegar allí. Eliminando / oscureciendo el enlace o botón que podría llevarlos a esta página no autorizada.

Probablemente sería bueno tener un parámetro adicional en el atributo para especificar dónde redirigir a un usuario no autorizado. Pero mientras tanto, veo el atributo Autorizeattribute como una red de seguridad.

 4
Author: Keltex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-27 19:57:02

Pruebe esto en el manejador Application_EndRequest de su Global.ascx file

if (HttpContext.Current.Response.Status.StartsWith("302") && HttpContext.Current.Request.Url.ToString().Contains("/<restricted_path>/"))
{
    HttpContext.Current.Response.ClearContent();
    Response.Redirect("~/AccessDenied.aspx");
}
 0
Author: Kareem Cambridge,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-30 05:53:23

Si está usando aspnetcore 2.0, use esto:

using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace Core
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public class AuthorizeApiAttribute : Microsoft.AspNetCore.Authorization.AuthorizeAttribute, IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            var user = context.HttpContext.User;

            if (!user.Identity.IsAuthenticated)
            {
                context.Result = new UnauthorizedResult();
                return;
            }
        }
    }
}
 0
Author: Greg0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-14 23:05:55