Por que AuthorizeAttribute redirecionar para a página de login para falhas de autenticação e autorização?

StackOverflow https://stackoverflow.com/questions/238437

Pergunta

Em ASP.NET MVC, você pode marcar um método controlador com AuthorizeAttribute, como este:

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

Isto significa que, se o momento usuário conectado não é no papel "CanDeleteTags", o método de controlador nunca será chamado.

Infelizmente, as falhas, AuthorizeAttribute retornos HttpUnauthorizedResult, que retorna sempre código de status HTTP 401. Isso faz com que um redirecionamento para a página de login.

Se o usuário não está logado, isso faz sentido. No entanto, se o usuário é logado, mas não está no papel necessário, é confuso para enviá-los de volta para a página de login.

Parece que a autenticação e autorização conflates AuthorizeAttribute.

Este parece ser um pouco de um descuido na ASP.NET MVC, ou estou faltando alguma coisa?

Eu tive que preparar um DemandRoleAttribute que separa os dois. Quando o usuário não é autenticado, ele retorna HTTP 401, enviá-los para a página de login. Quando o usuário está conectado, mas não está no papel necessário, ele cria um NotAuthorizedResult vez. Atualmente esta redirecionamentos para uma página de erro.

Com certeza eu não tenho que fazer isso?

Foi útil?

Solução

Quando foi desenvolvido pela primeira vez, System.Web.Mvc.AuthorizeAttribute estava fazendo a coisa certa - versões antigas da especificação HTTP usado código de status 401 para ambos "não autorizado" e "não autenticado".

A partir da especificação original:

Se o pedido já incluído credenciais de autorização, então a resposta 401 indica que a autorização foi recusado por essas credenciais.

Na verdade, você pode ver a confusão ali - ele usa a palavra "autorização" quando significa "autenticação". Na prática, todos os dias, no entanto, faz mais sentido para voltar a 403 proibido quando o usuário é autenticado, mas não autorizado. É improvável que o usuário teria um segundo conjunto de credenciais que lhes daria acesso -. Má experiência do usuário em todo

Considere a maioria dos sistemas operacionais - quando você tenta ler um arquivo que você não tem permissão para acessar, você não é mostrado uma tela de login!

Felizmente, as especificações HTTP foram atualizados (Junho de 2014) para remover a ambigüidade.

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

A (não autorizada) código de status 401 indica que o pedido não foi aplicada porque carece de credenciais de autenticação válidas para o recurso de destino.

De "Hypertext Transfer Protocol (HTTP / 1.1): Semântica e conteúdo" (RFC 7231):

A (proibido) código de status 403 indica que o servidor entendeu o pedido, mas se recusa a autorizá-lo.

Curiosamente, no momento ASP.NET MVC 1 foi lançado o comportamento de AuthorizeAttribute estava correta. Agora, o comportamento é incorreto -. A especificação HTTP / 1.1 foi corrigido

Ao invés de tentar mudar redirecionamentos de páginas de login do ASP.NET, é mais fácil apenas para corrigir o problema na fonte. Você pode criar um novo atributo com o mesmo nome (AuthorizeAttribute) em padrão do seu site namespace (isto é muito importante), então o compilador irá automaticamente pegá-lo em vez de um padrão de MVC. Claro, você pode sempre dar o atributo um novo nome se você prefere tomar essa abordagem.

[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);
        }
    }
}

Outras dicas

Adicione isto a sua função de login Page_Load:

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

Quando o usuário é redirecionado lá, mas já está logado, mostra a página não autorizada. Se eles não está logado, cai completamente e mostra a página de login.

Eu sempre pensei que isso fazia sentido. Se você está logado e você tentar acertar uma página que requer um papel que você não tem, você é encaminhado para a tela de login pedindo que você faça login com um usuário que tem o papel.

Você pode adicionar lógica para a página de login que verifica se o usuário já está autenticado. Você pode adicionar uma mensagem amigável que explica por que eles foram bumbed voltar lá novamente.

Infelizmente, você está lidando com o comportamento padrão de autenticação de formulários ASP.NET. Existe uma solução (eu não tentei) discutido aqui:

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

(Não é específico para MVC)

Eu acho que na maioria dos casos, a melhor solução é restringir o acesso a recursos não autorizados antes de o usuário tentar chegar lá. Ao remover / grisalho o link ou botão que pode levá-los a esta página não autorizada.

Provavelmente seria bom ter um parâmetro adicional no atributo para especificar para onde redirecionar um usuário não autorizado. Mas, enquanto isso, eu olhar para o AuthorizeAttribute como uma rede de segurança.

Tente isto em seu no manipulador Application_EndRequest do seu arquivo Global.ascx

if (HttpContext.Current.Response.Status.StartsWith("302") && HttpContext.Current.Request.Url.ToString().Contains("/<restricted_path>/"))
{
    HttpContext.Current.Response.ClearContent();
    Response.Redirect("~/AccessDenied.aspx");
}

Se o seu usando aspnetcore 2.0, use o seguinte:

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;
            }
        }
    }
}

No meu caso o problema era "HTTP especificação de código de status usada 401 para ambos 'não autorizado' e 'não autenticado'". Como disse shadowchaser.

Esta solução funciona para mim:

if (User != null &&  User.Identity.IsAuthenticated && Response.StatusCode == 401)
{
    //Do whatever

    //In my case redirect to error page
    Response.RedirectToRoute("Default", new { controller = "Home", action = "ErrorUnauthorized" });
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top