Почему AuthorizeAttribute перенаправляется на страницу входа в случае сбоев аутентификации и авторизации?

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

Вопрос

В ASP.NET MVC вы можете пометить метод контроллера с помощью AuthorizeAttribute, так:

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

Это означает, что если текущий пользователь, вошедший в систему, не имеет роли CanDeleteTags, метод контроллера никогда не будет вызван.

К сожалению, из-за неудач AuthorizeAttribute возвращает HttpUnauthorizedResult, который всегда возвращает код состояния HTTP 401.Это вызывает перенаправление на страницу входа.

Если пользователь не вошел в систему, это имеет смысл.Однако, если пользователь уже вошли в систему, но не имеют требуемой роли, их сложно отправить обратно на страницу входа.

Кажется, что AuthorizeAttribute объединяет аутентификацию и авторизацию.

Это похоже на недосмотр в ASP.NET MVC, или я что-то упускаю?

мне пришлось приготовить DemandRoleAttribute это разделяет их.Если пользователь не прошел аутентификацию, он возвращает HTTP 401, отправляя его на страницу входа.Когда пользователь вошел в систему, но не имеет требуемой роли, он создает NotAuthorizedResult вместо.В настоящее время это перенаправляет на страницу с ошибкой.

Неужели мне не пришлось этого делать?

Это было полезно?

Решение

Когда он был впервые разработан, System.Web.mvc.authorizeatTribute поступала правильно - более старые изменения спецификации HTTP использовали код состояния 401 как для «несанкционированного», так и для «неавтомированного».

Из оригинальной спецификации:

Если запрос уже включал учетные данные авторизации, то ответ 401 указывает, что в авторизации для этих учетных данных было отказано.

На самом деле, тут же можно увидеть путаницу — слово «авторизация» используется там, где оно означает «аутентификация».Однако в повседневной практике имеет смысл возвращать 403 Forbidden, когда пользователь аутентифицирован, но не авторизован.Маловероятно, что у пользователя будет второй набор учетных данных, который предоставит ему доступ — повсюду плохой пользовательский опыт.

Рассмотрим большинство операционных систем: когда вы пытаетесь прочитать файл, на доступ к которому у вас нет разрешения, вам не отображается экран входа в систему!

К счастью, спецификации HTTP были обновлены (июнь 2014 г.), чтобы устранить двусмысленность.

Из «Протокола передачи гипертекста (HTTP/1.1):Аутентификация» (RFC 7235):

Код состояния 401 (неавторизованный) указывает, что запрос не был применен, поскольку у него отсутствуют действительные учетные данные аутентификации для целевого ресурса.

Из «Протокола передачи гипертекста (HTTP/1.1):Семантика и контент» (RFC 7231):

Код состояния 403 (Запрещено) указывает на то, что сервер понял запрос, но отказывается его авторизовать.

Интересно, что на момент выпуска ASP.NET MVC 1 поведение AuthorizeAttribute было правильным.Теперь поведение неверное — исправлена ​​спецификация HTTP/1.1.

Вместо того, чтобы пытаться изменить перенаправление страницы входа в систему ASP.NET, проще устранить проблему в источнике.Вы можете создать новый атрибут с тем же именем (AuthorizeAttribute) в пространстве имен вашего сайта по умолчанию (это очень важно) тогда компилятор автоматически подберет его вместо стандартного MVC.Конечно, вы всегда можете дать атрибуту новое имя, если предпочитаете такой подход.

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

Другие советы

Добавьте это в вашу функцию Page_Load для входа в систему:

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

Когда пользователь перенаправлен туда, но уже вошел в систему, отображается неавторизованная страница. Если они не вошли в систему, он провалится и покажет страницу входа.

Я всегда думал, что это имеет смысл. Если вы вошли в систему и пытаетесь перейти на страницу, для которой не нужна роль, вы попадаете на экран входа в систему с просьбой войти в систему с пользователем, у которого есть эта роль.

Вы можете добавить логику на страницу входа, которая проверяет, прошел ли аутентификация пользователя. Вы можете добавить дружеское сообщение, которое объясняет, почему они снова были обмануты.

К сожалению, вы имеете дело с поведением по умолчанию проверки подлинности форм ASP.NET. Здесь есть обходной путь (я не пробовал):

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

(это не относится к MVC)

Я думаю, что в большинстве случаев лучшим решением является ограничение доступа к неавторизованным ресурсам до того, как пользователь попытается туда попасть. Удаляя / скрывая ссылку или кнопку, которая может привести их к этой неавторизованной странице.

Вероятно, было бы неплохо иметь дополнительный параметр в атрибуте, чтобы указать, куда перенаправлять неавторизованного пользователя. Но пока я смотрю на AuthorizeAttribute как на сеть безопасности.

Попробуйте это сделать в обработчике Application_EndRequest вашего файла Global.asax

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

Если вы используете aspnetcore 2.0, используйте это:

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

В моем случае проблема заключалась в том, что "спецификация HTTP использовала код состояния 401 для обоих случаев" неавторизован ". и "не прошедший проверку подлинности". Как сказал ShadowChaser.

Это решение работает для меня:

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" });
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top