2008-10-26 8 views
234

В ASP.NET MVC, вы можете разметить метод контроллера с AuthorizeAttribute, например:Почему AuthorizeAttribute перенаправляет на страницу входа для проверки подлинности и авторизации?

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

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

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

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

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

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

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

Неужели мне не нужно было это делать?

+7

Отличный вопрос, и я согласен, должен выдавать статус HTTP Not Authorized. – 2010-06-06 04:13:41

+2

Мне нравится ваше решение, Роджер. Даже если вы этого не сделаете. – 2011-01-07 11:43:52

+0

На моей странице входа есть проверка, чтобы просто перенаправить пользователя в ReturnUrl, если он/она уже автономно. Поэтому мне удалось создать бесконечный цикл из 302 переадресаций: D woot. – 2011-08-24 12:14:10

ответ

282

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

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

If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials.

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

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

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

От "Hyper Text Transport Protocol (HTTP/1.1): Аутентификация" (RFC 7235):

The 401 (Unauthorized) status code indicates that the request has not been applied because it lacks valid authentication credentials for the target resource.

От "протокола передачи гипертекста (HTTP/1.1): семантика и содержание" (RFC 7231):

The 403 (Forbidden) status code indicates that the server understood the request but refuses to authorize it.

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

Вместо того чтобы пытаться изменить перенаправление ASP.NET на страницу входа в систему, проще всего исправить проблему в источнике. Вы можете создать новый атрибут с тем же именем() в пространстве имен по умолчанию вашего сайта (это очень важно), тогда компилятор автоматически подберет его вместо стандартного 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); 
     } 
    } 
} 
4

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

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

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

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

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

4

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

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

23

Добавьте это в Вход функции Page_Load:

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

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

-1

Попробуйте это в в обработчике Application_EndRequest вашего 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"); 
} 
0

Если вы используете 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; 
      } 
     } 
    } 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^