Проверка подлинности с помощью форм: отключить перенаправление на страницу входа
у меня есть приложение, которое использует ASP.NET проверка подлинности с помощью форм. По большей части, он отлично работает, но я пытаюсь добавить поддержку простого API через an .файл ashx. Я хочу, чтобы файл ashx имел дополнительную аутентификацию (т. е. если вы не предоставляете заголовок аутентификации, то он просто работает анонимно). Но, в зависимости от того, что вы делаете, я хочу требовать аутентификации при определенных условиях.
Я думал, что это будет простой вопрос, ответа с кодом состояния 401 если необходимая аутентификация не была предоставлена, но похоже, что модуль Authentcation Forms перехватывает это и вместо этого отвечает перенаправлением на страницу входа. Я имею в виду, если мой ProcessRequest
способ выглядит так:
public void ProcessRequest(HttpContext context)
{
Response.StatusCode = 401;
Response.StatusDescription = "Authentication required";
}
затем вместо того, чтобы получить код ошибки 401 на клиенте, как я ожидаю, я на самом деле получение 302 перенаправления на страницу входа.
для nornal HTTP-трафик, я вижу, как это может быть полезно, но для моих API страница, я хочу, чтобы 401 прошел без изменений, чтобы клиентский вызывающий абонент мог отвечать на него программно.
есть ли способ сделать это?
10 ответов:
ASP.NET 4.5 добавлено логическое значение
HttpResponse.SuppressFormsAuthenticationRedirect
собственность.public void ProcessRequest(HttpContext context) { Response.StatusCode = 401; Response.StatusDescription = "Authentication required"; Response.SuppressFormsAuthenticationRedirect = true; }
после небольшого расследования, это выглядит как FormsAuthenticationModule добавляет обработчик для элемента
HttpApplicationContext.EndRequest
событие. В его обработчике он проверяет код состояния 401 и в основном делает . Насколько я могу судить, нет никакого способа переопределить это поведение, если вы хотите использоватьFormsAuthenticationModule
.то, как я в конечном итоге обойти это было путем отключения
FormsAuthenticationModule
в интернете.конфиг вот такой:<authentication mode="None" />
и затем реализовать
Application_AuthenticateEvent
я сам:void Application_AuthenticateRequest(object sender, EventArgs e) { if (Context.User == null) { var oldTicket = ExtractTicketFromCookie(Context, FormsAuthentication.FormsCookieName); if (oldTicket != null && !oldTicket.Expired) { var ticket = oldTicket; if (FormsAuthentication.SlidingExpiration) { ticket = FormsAuthentication.RenewTicketIfOld(oldTicket); if (ticket == null) return; } Context.User = new GenericPrincipal(new FormsIdentity(ticket), new string[0]); if (ticket != oldTicket) { // update the cookie since we've refreshed the ticket string cookieValue = FormsAuthentication.Encrypt(ticket); var cookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName] ?? new HttpCookie(FormsAuthentication.FormsCookieName, cookieValue) { Path = ticket.CookiePath }; if (ticket.IsPersistent) cookie.Expires = ticket.Expiration; cookie.Value = cookieValue; cookie.Secure = FormsAuthentication.RequireSSL; cookie.HttpOnly = true; if (FormsAuthentication.CookieDomain != null) cookie.Domain = FormsAuthentication.CookieDomain; Context.Response.Cookies.Remove(cookie.Name); Context.Response.Cookies.Add(cookie); } } } } private static FormsAuthenticationTicket ExtractTicketFromCookie(HttpContext context, string name) { FormsAuthenticationTicket ticket = null; string encryptedTicket = null; var cookie = context.Request.Cookies[name]; if (cookie != null) { encryptedTicket = cookie.Value; } if (!string.IsNullOrEmpty(encryptedTicket)) { try { ticket = FormsAuthentication.Decrypt(encryptedTicket); } catch { context.Request.Cookies.Remove(name); } if (ticket != null && !ticket.Expired) { return ticket; } // if the ticket is expired then remove it context.Request.Cookies.Remove(name); return null; } }
это на самом деле немного сложнее, чем это, но я в основном получил код, глядя на реализацию
FormsAuthenticationModule
в отражатель. Моя реализация отличается от встроеннойFormsAuthenticationModule
в том, что он ничего не делает, если вы отвечаете 401 - нет перенаправления на страницу входа вообще. Я думаю, если это когда-нибудь станет требованием, я могу поместить элемент в контекст, чтобы отключить автоматическое перенаправление или что-то еще.
Я не уверен, что это будет работать для всех, но в IIS7 вы можете вызвать ответ.End() после установки кода состояния и описания. Таким образом, что #&$^#@*! FormsAuthenticationModule не будет делать перенаправление.
public void ProcessRequest(HttpContext context) { Response.StatusCode = 401; Response.StatusDescription = "Authentication required"; Response.End(); }
чтобы немного опираться на ответ захаридла, я использовал это, чтобы решить свои проблемы. На каждый запрос, в начале, если это AJAX, немедленно подавить поведение.
protected void Application_BeginRequest() { HttpRequestBase request = new HttpRequestWrapper(Context.Request); if (request.IsAjaxRequest()) { Context.Response.SuppressFormsAuthenticationRedirect = true; } }
Я не знаю, как этот ответ.End() работал на вас. Я попробовал это без радости, затем посмотрел на MSDN для ответа.End ():'останавливает выполнение страницы и вызывает событие EndRequest'.
для чего это стоит мой Хак был:
_response.StatusCode = 401; _context.Items["401Override"] = true; _response.End();
затем в глобальные.cs добавить обработчик EndRequest (который будет вызван после аутентификации HTTPModule):
protected void Application_EndRequest(object sender, EventArgs e) { if (HttpContext.Current.Items["401Override"] != null) { HttpContext.Current.Response.Clear(); HttpContext.Current.Response.StatusCode = 401; } }
Я знаю, что есть уже ответ с галочкой, но при попытке решить подобную проблему я нашел этой (http://blog.inedo.com/2010/10/12/http-418-im-a-teapot-finally-a-%e2%80%9clegitimate%e2%80%9d-use/) в качестве альтернативы.
В основном вы возвращаете свой собственный код состояния HTTP (например, 418) в своем коде. В моем случае служба данных WCF.
throw new DataServiceException(418, "401 Unauthorized");
затем используйте HTTP-модуль для обработки его в
EndRequest
событие для перезаписи кода обратно 401.HttpApplication app = (HttpApplication)sender; if (app.Context.Response.StatusCode == 418) { app.Context.Response.StatusCode = 401; }
браузер / клиент получит правильный контент и код состояния, он отлично работает для меня:)
Если вы заинтересованы, чтобы узнать больше о HTTP-код состояния 418 см этот вопрос и ответ.
то, что вы узнали, правильно о формах auth, перехватывающих 401 и выполняющих перенаправление, но мы также можем сделать это, чтобы отменить это.
в основном вам нужен http-модуль для перехвата перенаправления 302 на страницу входа и обратного его на 401.
шаги по выполнению этого объяснены в здесь
данная ссылка относится к службе WCF, но она одинакова во всех сценариях проверки подлинности форм.
как пояснил в приведенной выше ссылке Вам также нужно очистить заголовки http, но не забудьте вернуть заголовок cookie в ответ, если исходный ответ (т. е. до перехвата) содержал какие-либо файлы cookie.
Это известная проблема, и есть Пакет NuGet для этого и/или исходный код доступен.