Как MVC может вернуть неавторизованный код без перенаправления в представление входа в систему - PullRequest
24 голосов
/ 10 сентября 2010

Мое веб-приложение 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 ]

26 голосов
/ 13 апреля 2013

Чтобы предотвратить перенаправление страницы входа, необходимо установить для SuppressFormsAuthenticationRedirect свойство HttpContext.Response значение true;

 HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
3 голосов
/ 11 сентября 2010

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

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

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

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

2 голосов
/ 15 января 2013

Вы можете создать простой фильтр атрибутов авторизации (расширить класс 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 подберет и затем перенаправит ваш ответ на страницу входа.

1 голос
/ 05 мая 2011

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

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

protected void Application_EndRequest()
{
    if (Context.Response.StatusCode == 302 && Context.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
    {
        Context.Response.Clear();
        Context.Response.StatusCode = 401;
    }
}
0 голосов
/ 28 марта 2017

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

0 голосов
/ 13 апреля 2013

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

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

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

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

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

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

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

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

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

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

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

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