表单身份验证:禁用重定向到登录页面

Dea*_*ing 69 asp.net forms-authentication

我有一个使用ASP.NET Forms身份验证的应用程序.在大多数情况下,它工作得很好,但我试图通过.ashx文件添加对简单API的支持.我希望ashx文件具有可选的身份验证(即,如果您不提供身份验证标头,那么它只是匿名工作).但是,根据您的操作,我希望在某些条件下要求身份验证.

如果没有提供所需的身份验证,我认为用状态代码401进行响应将是一件简单的事情,但似乎Forms Authentcation模块正在拦截它并通过重定向到登录页面进行响应.我的意思是,如果我的ProcessRequest方法看起来像这样:

public void ProcessRequest(HttpContext context)
{
    Response.StatusCode = 401;
    Response.StatusDescription = "Authentication required";
}
Run Code Online (Sandbox Code Playgroud)

然后,而不是像我期望的那样在客户端上获得401错误代码,我实际上正在获得302重定向到登录页面.

对于nornal HTTP流量,我可以看到它是如何有用的,但是对于我的API页面,我希望401经过不加修改,以便客户端调用者可以以编程方式响应它.

有没有办法做到这一点?

zac*_*ydl 74

ASP.NET 4.5添加了Boolean HttpResponse.SuppressFormsAuthenticationRedirect属性.

public void ProcessRequest(HttpContext context)
{
    Response.StatusCode = 401;
    Response.StatusDescription = "Authentication required";
    Response.SuppressFormsAuthenticationRedirect = true;
}
Run Code Online (Sandbox Code Playgroud)

  • 我在Application_BeginRequest方法中使用了类似的代码段并取得了巨大的成功.请求是AJAX吗?立即设置标志. (4认同)
  • 您可以将它放在Global.asax`Application_EndRequest`事件中,以在整个应用程序中应用它.[例如.](http://stackoverflow.com/a/18519725/188246) (2认同)

Dea*_*ing 35

经过一些调查后,看起来FormsAuthenticationModuleHttpApplicationContext.EndRequest事件添加了一个处理程序.在它的处理程序中,它检查401状态代码,并基本上做了一个Response.Redirect(loginUrl).据我所知,如果想要使用,就没有办法覆盖这种行为FormsAuthenticationModule.

我最终绕过它的方式是FormsAuthenticationModule在web.config中禁用如下:

<authentication mode="None" />
Run Code Online (Sandbox Code Playgroud)

然后实现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;
    }
}
Run Code Online (Sandbox Code Playgroud)

它实际上稍微复杂一点,但我基本上通过查看FormsAuthenticationModule反射器的实现来获得代码.我的实现与内置的不同之处FormsAuthenticationModule在于,如果您使用401响应它不会执行任何操作 - 根本不会重定向到登录页面.我想如果这成为一个要求,我可以在上下文中放置一个项目来禁用自动重定向或其他东西.

  • 注意:上面的[回答zacharydl](http://stackoverflow.com/a/16846041/241462)是ASP.NET 4.5+的方法 (2认同)

Luk*_*son 11

我不确定这是否适用于所有人,但在IIS7中,您可以在设置状态代码和描述后调用Response.End().这样,#&$ ^#@*!FormsAuthenticationModule不会执行重定向.

public void ProcessRequest(HttpContext context) {
    Response.StatusCode = 401;
    Response.StatusDescription = "Authentication required";
    Response.End();
}
Run Code Online (Sandbox Code Playgroud)

  • 好一个约翰!我们有多难努力解决这个问题,这难道不是很荒谬吗?HttpContext有一个SkipAuthorization属性,如果它也有SkipLoginRedirect会很好.或者,如果表单身份验证可以由目录进行范围限制和覆盖. (3认同)
  • 这是有效的,但无论如何控制将转到FormsAuthenticationModule,它的`EndRequest`事件处理程序将尝试进行重定向,并且由于响应已经是`End()`ed,这会触发系统事件日志中记录的异常,调用`Application_Error()`. (2认同)

Tyl*_*the 7

为了稍微建立zacharydl的答案,我用它来解决我的困境.在每个请求,在开始时,如果它是AJAX,立即抑制行为.

protected void Application_BeginRequest()
{
    HttpRequestBase request = new HttpRequestWrapper(Context.Request);
    if (request.IsAjaxRequest())
    {
        Context.Response.SuppressFormsAuthenticationRedirect = true;
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

我不知道Response.End()是如何为你工作的.我毫不高兴地尝试了它,然后查看了MSDN的Response.End():'停止执行页面,并引发EndRequest事件'.

对于我的黑客的价值是:

_response.StatusCode = 401;
_context.Items["401Override"] = true;
_response.End();
Run Code Online (Sandbox Code Playgroud)

然后在Global.cs中添加一个EndRequest处理程序(将在Authentication HTTPModule之后调用):

protected void Application_EndRequest(object sender, EventArgs e)
{
    if (HttpContext.Current.Items["401Override"] != null)
    {
        HttpContext.Current.Response.Clear();
        HttpContext.Current.Response.StatusCode = 401;
    }
}
Run Code Online (Sandbox Code Playgroud)


air*_*x86 5

我知道已经有了回答,但在尝试解决类似的问题时,我发现了这个问题(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");
Run Code Online (Sandbox Code Playgroud)

然后使用HTTP模块在EndRequest事件处理它以将代码重写回401.

HttpApplication app = (HttpApplication)sender;
if (app.Context.Response.StatusCode == 418)
{
    app.Context.Response.StatusCode = 401;
}
Run Code Online (Sandbox Code Playgroud)

浏览器/客户端将收到正确的内容和状态代码,它对我很有用:)

如果您有兴趣了解有关HTTP状态代码418的更多信息,请参阅此问题和答案.