Проверка подлинности с помощью форм: отключить перенаправление на страницу входа


у меня есть приложение, которое использует 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 64

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 для этого и/или исходный код доступен.

вы не устанавливаете WWW-Authenticate заголовок в коде, который вы показываете, поэтому клиент не может выполнять проверку подлинности HTTP вместо проверки подлинности форм. Если это так, вы должны использовать 403 вместо 401, который не будет перехвачен FormsAuthenticaitonModule.

загляните в свою паутину.конфигурационный файл в configuration\authentication. Если есть forms подэлемент там с loginUrl атрибут, удалите его и повторите попытку.