В Asp.Net MVC 2 есть лучший способ вернуть коды состояния 401 без перенаправления авторизации - PullRequest
7 голосов
/ 12 мая 2010

У меня есть часть моего сайта с легким API-интерфейсом REST xml / json. Большая часть моего сайта находится за аутентификацией форм, но только некоторые из моих действий API требуют аутентификации.

У меня есть собственный AuthorizeAttribute для моего API, который я использую для проверки на наличие определенных разрешений, и в случае сбоя это приводит к 401. Все хорошо, за исключением того, что я использую формы auth, Asp.net удобно конвертирует это в 302 перенаправить на мою страницу входа.

Я видел несколько предыдущих вопросов , которые кажутся немного хакерскими, чтобы либо вместо этого возвращать 403, либо использовать некоторую логику в global.asax protected void Application_EndRequest () это, по сути, преобразует 302 в 401, где оно соответствует любым критериям.

То, что я сейчас делаю, похоже на один из вопросов, но вместо проверки Application_EndRequest () для 302 я возвращаю свой атрибут авторизации 666 , который указывает мне, что мне нужно установить это на 401.

Вот мой код:

protected void Application_EndRequest()
{
  if (Context.Response.StatusCode == MyAuthAttribute.AUTHORIZATION_FAILED_STATUS)
   {   
       //check for 666 - status code of hidden 401
        Context.Response.StatusCode = 401;
    }
 }

Даже при том, что это работает, мой вопрос, есть ли что-то в Asp.net MVC 2, что помешало бы мне сделать это? Или, вообще, есть ли лучший способ? Я бы подумал, что это подойдет многим для тех, кто работает с REST API, или просто для тех, кто выполняет запросы AJAX в своих контроллерах. Последнее, что вам нужно, это сделать запрос и получить содержимое страницы входа вместо json.

Ответы [ 5 ]

5 голосов
/ 12 мая 2010

Как насчет украшения вашего контроллера / действий с помощью пользовательского фильтра:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class RequiresAuthenticationAttribute : FilterAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        var user = filterContext.HttpContext.User;
        if (!user.Identity.IsAuthenticated)
        {
            filterContext.HttpContext.Response.StatusCode = 401;
            filterContext.HttpContext.Response.End();
        }
    }
}

и в вашем контроллере:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [RequiresAuthentication]
    public ActionResult AuthenticatedIndex()
    {
        return View();
    }
}
2 голосов
/ 20 марта 2011

Еще один способ сделать это - реализовать пользовательский ActionResult. В моем случае я все равно хотел один, так как я хотел простой способ отправки данных с пользовательскими заголовками и кодами ответов (для REST API). Я нашел идею сделать DelegatingActionResult и просто добавил к нему вызов Response.End(). Вот результат:

public class DelegatingActionResult : ActionResult
{
    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        Command(context);
        // prevent ASP.Net from hijacking our headers
        context.HttpContext.Response.End();
    }

    private readonly Action<ControllerContext> Command;

    public DelegatingActionResult(Action<ControllerContext> command)
    {
        if (command == null)
            throw new ArgumentNullException("command");

        Command = command;
    }
}
0 голосов
/ 24 февраля 2011

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

protected void Application_EndRequest()
{
  if (Context.Response.StatusCode == MyAuthAttribute.AUTHORIZATION_FAILED_STATUS)
   {   
       //check for 666 - status code of hidden 401
        Context.Response.StatusCode = 401;
    }
 }
0 голосов
/ 19 мая 2010

TurnOffTheRedirectionAtIIS

Из MSDN, В этой статье объясняется, как избежать перенаправления ответов 401 :)

Приводя:

Используя диспетчер IIS, щелкните правой кнопкой мыши WinLogin.aspx файл, нажмите Свойства, а затем перейдите на вкладку Custom Errors Редактировать различные ошибки 401 и назначить пользовательское перенаправление. К сожалению, это перенаправление должно быть статическим файлом - он не будет обрабатываться страница ASP.NET. Мое решение состоит в том, чтобы перенаправление на статический Redirect401.htm файл с полным физическим путем, который содержит JavaScript или метатег, для перенаправления на реальный ASP.NET форма входа в систему, названная WebLogin.aspx. Обратите внимание, что вы потеряете оригинальный ReturnUrl в этих перенаправления, так как ошибка IIS перенаправление требует статического HTML файл с ничего динамическим, так что вы будете придется разобраться с этим позже.

Надеюсь, это поможет вам.

0 голосов
/ 19 мая 2010

Самое простое и чистое решение, которое я нашел для этого, - зарегистрировать обратный вызов с помощью события jQuery.ajaxSuccess () и проверить наличие заголовка ответа «X-AspNetMvc-Version».

Каждый запрос jQuery Ajax в моем приложении обрабатывается Mvc, поэтому, если отсутствует заголовок, я знаю, что мой запрос перенаправлен на страницу входа, и я просто перезагружаю страницу для перенаправления верхнего уровня:

 $(document).ajaxSuccess(function(event, XMLHttpRequest, ajaxOptions) {
    // if request returns non MVC page reload because this means the user 
    // session has expired
    var mvcHeaderName = "X-AspNetMvc-Version";
    var mvcHeaderValue = XMLHttpRequest.getResponseHeader(mvcHeaderName);

    if (!mvcHeaderValue) {
        location.reload();
    }
});

Перезагрузка страницы может вызвать некоторые ошибки Javascript (в зависимости от того, что вы делаете с ответом Ajax), но в большинстве случаев, когда отладка отключена, пользователь никогда не увидит их.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...