Как MVC может вернуть неавторизованный код без перенаправления в представление входа в систему

Мое веб-приложение MVC обслуживает два типа пользователей.

  • Первый над стандартным веб-браузером;
  • Второй после REST, возвращающий только данные JSON.

Дополнительно,

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

Когда пользователи получают доступ к приложению, если они не вошли в систему, приложение должно реагировать по-другому.

  1. В первом случае он должен вернуть страницу входа по умолчанию (это нормально).
  2. Во втором случае он должен возвращать только 401 несанкционированный код.

Я привык работать со службой WCF REST, где я мог бы вызвать исключение, подобное этому:

throw new WebProtocolException(System.Net.HttpStatusCode.Unauthorized, exc.Message, exc);

и получите сообщение 401. Проблема с тем же подходом в MVC, когда я ставлю statusCode как это:

HttpContext.Response.StatusCode = (Int32)HttpStatusCode.Unauthorized

он всегда перенаправляет на страницу входа.

Как я могу это сделать?

Я пытался переопределить AuthorizeAttribute и обработка OnAuthorization функция, но все же, как только я установлю statusCode на 401 он перенаправляется на страницу входа.

6 ответов

Чтобы предотвратить перенаправление страницы входа в систему, вы должны установить SuppressFormsAuthenticationRedirect собственностью HttpContext.Response к истине;

 HttpContext.Response.SuppressFormsAuthenticationRedirect = true;

То, что вы испытываете, является дырой в ASP.NET MVC (я надеюсь, что они исправят один день).

Стандартная операционная модель для ASP.NET заключается в том, что если обнаружен код статуса 401 Http, то при возникновении этого он автоматически перенаправляется на страницу входа в систему, и это происходит, даже если вы вошли через вызов Ajax. К сожалению, я также не нашел способа изменить это поведение.

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

Поэтому в моем фильтре проверки подлинности, если это Ajax-запрос, я возвращаю 449, в противном случае стандарт 401. Затем на клиенте я могу просмотреть XMLHttpRequest.status и предпринять соответствующие действия, если обнаружен 449.

Вы можете создать простой фильтр атрибутов авторизации (расширить класс AuthorizeAttribute) и использовать его для контроля доступа. Затем попробуйте что-то вроде этого:

protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
    if (filterContext.HttpContext.Request.IsAjaxRequest() ||  string.Compare("GET", filterContext.HttpContext.Request.HttpMethod, true) != 0)
    {
        // Returns 403.
        filterContext.Result = new HttpStatusCodeResult((int)HttpStatusCode.Forbidden);
    }
    else
    {
        // Returns 401.
        filterContext.Result = new HttpUnauthorizedResult();
    }
}

В результате запросы POST и AJAX всегда будут получать ответ 403, что облегчит вам обработку ваших ajax-запросов в javascript. Что касается постов, не относящихся к AJAX, то, на самом деле, не имеет значения, какой ответ, потому что ваш пользователь не должен был в первую очередь получить форму отправки:)

Что касается других запросов, метод возвращает 401, что модуль formsAuthentiction подберет и затем перенаправит ваш ответ на страницу входа.

Именно так мне удалось предотвратить перенаправление на страницу входа.

В моем случае, когда я хотел получить код состояния, чтобы обработать его в JavaScript:

protected void Application_EndRequest()
{
    if (Context.Response.StatusCode == 302 && Context.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
    {
        Context.Response.Clear();
        Context.Response.StatusCode = 401;
    }
}

Вы можете использовать метод OnRedirectToLogin при настройке идентичности.

Я работаю над веб-приложением, которое также предоставляет веб-API, и я не вижу такого поведения.

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

Этот механизм может быть:

  • Отправка ваших учетных данных с каждым запросом. Обычно используется Http Basic Authentication.
  • Предоставьте конечную точку аутентификации, которую можно один раз получить с надлежащими учетными данными, и пользователь получит токен / билет в результате, если аутентификация пройдет успешно.

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

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

Этого можно достичь в Asp.net Web Api с помощью обработчика сообщений, который называется DelegatingHandler, который будет обрабатывать каждый запрос к вашим контроллерам API и заполнять ваш Принципал или нет, основываясь на информации аутентификации, которую он находит.

Вы можете найти пример реализации DelegatingHandler здесь

После того, как вы создадите эту инфраструктуру, вы сможете использовать Syste.Web.Http.AuthorizeAttribute aka [Authorize] на уровне контроллера api или на уровне действий, в соответствии с вашими потребностями.

Вы должны получить 401 Unauthorized w / oa redirection, если DelegatingHandler не может заполнить принципал, учитывая имеющуюся у него информацию об аутентификации.

Не забудьте зарегистрировать свой DelegatingHandler в App_Start.

Другие вопросы по тегам