Почему AuthorizeAttribute перенаправляется на страницу входа в случае сбоев аутентификации и авторизации?
-
04-07-2019 - |
Вопрос
В 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" });
}