代码之家  ›  专栏  ›  技术社区  ›  Dean Harding

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

  •  65
  • Dean Harding  · 技术社区  · 15 年前

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

    我认为,如果没有提供所需的身份验证,那么使用状态代码401进行响应是一件简单的事情,但似乎表单授权模块正在截取该身份验证,并通过重定向到登录页面进行响应。我的意思是,如果我的 ProcessRequest 方法如下:

    public void ProcessRequest(HttpContext context)
    {
        Response.StatusCode = 401;
        Response.StatusDescription = "Authentication required";
    }
    

    然后,我不再像我预期的那样在客户机上获取401错误代码,而是 事实上 获取302重定向到登录页。

    对于NornalHTTP流量,我可以看到这是如何有用的,但是对于我的API页面,我希望401不经过修改,这样客户端调用程序就可以改为以编程方式响应它。

    有什么办法吗?

    11 回复  |  直到 6 年前
        1
  •  71
  •   zacharydl    12 年前

    ASP.NET 4.5添加了布尔值 HttpResponse.SuppressFormsAuthenticationRedirect 财产。

    public void ProcessRequest(HttpContext context)
    {
        Response.StatusCode = 401;
        Response.StatusDescription = "Authentication required";
        Response.SuppressFormsAuthenticationRedirect = true;
    }
    
        2
  •  35
  •   Dean Harding    15 年前

    经过一番调查,它看起来像 FormsAuthenticationModule 为添加处理程序 HttpApplicationContext.EndRequest 事件。在它的处理程序中,它检查401状态代码,并基本上执行 Response.Redirect(loginUrl) 相反。据我所知,如果想使用 FormsAuthenticationModule .

    我最终摆脱困境的方法是 表单身份验证模块 在web.config中,如下所示:

    <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;
        }
    }
    

    实际上比这稍微复杂一点,但我基本上是通过查看 表单身份验证模块 在反射器中。我的实现与内置的不同 表单身份验证模块 如果你用401-no重定向到登录页面,它就什么都不做了。我想如果这成为一个需求,我可以在上下文中放置一个项目来禁用自动重定向或其他功能。

        3
  •  11
  •   Luke Sampson    15 年前

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

    public void ProcessRequest(HttpContext context) {
        Response.StatusCode = 401;
        Response.StatusDescription = "Authentication required";
        Response.End();
    }
    
        4
  •  7
  •   Tyler Forsythe    12 年前

    为了稍微巩固扎卡里德尔的回答,我用这个来解决我的困境。对于每个请求,在开始时,如果是Ajax,则立即抑制该行为。

    protected void Application_BeginRequest()
    {
        HttpRequestBase request = new HttpRequestWrapper(Context.Request);
        if (request.IsAjaxRequest())
        {
            Context.Response.SuppressFormsAuthenticationRedirect = true;
        }
    }
    
        5
  •  5
  •   JaiB    15 年前

    我不知道response.end()是如何为您工作的。我毫无喜悦地尝试了它,然后查看msdn以获取响应。end():'停止页面的执行,并引发endrequest事件'。

    我的黑客值得一提的是:

    _response.StatusCode = 401;
    _context.Items["401Override"] = true;
    _response.End();
    

    然后在global.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;
        }
    }
    
        6
  •  5
  •   Community Mohan Dere    8 年前

    我知道已经有一个勾号的答案了,但当我试图解决类似的问题时,我发现 this ( 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的更多信息,请参阅 this question & answer .

        7
  •  4
  •   Amila    15 年前

    关于表单auth拦截401并执行重定向,您发现的是正确的,但是我们也可以这样做来逆转这一点。

    基本上,您需要的是一个HTTP模块来截获302重定向到登录页并将其反转到401。

    有关执行此操作的步骤,请参见 here

    给定的链接是关于WCF服务的,但在所有表单身份验证方案中都是相同的。

    正如上面链接中所解释的,您还需要清除HTTP头,但是如果原始响应(即截取前)包含任何cookie,请记住将cookie头放回响应。

        8
  •  2
  •   Bill the Lizard    14 年前

    这是一个已知的问题 NuGet Package 为此和/或 source code 可用。

        9
  •  0
  •   Jesper Kristensen    13 年前

    你不设置 WWW-Authenticate 您所显示的代码中的头,因此客户端不能执行HTTP身份验证而不是表单身份验证。如果是这种情况,您应该使用403而不是401,这将不会被 FormsAuthenticaitonModule .

        10
  •  0
  •   German Latorre    6 年前

    如果您使用.NET Framework>=v4.0但<v4.5,则会出现有趣的黑客行为。它使用 反射 设置不可访问的值 SuppressFormsAuthenticationRedirect 财产:

    // Set property to "true" using reflection
    Response
      .GetType()
      .GetProperty("SuppressFormsAuthenticationRedirect")
      .SetValue(Response, true, null);
    
        11
  •  -2
  •   Shay Friedman    15 年前

    在中查看web.config文件 configuration\authentication . 如果有 forms 其中的子元素 loginUrl 属性,删除它并重试。

    推荐文章