Forms authentication: disable redirect to the login page

后端 未结 12 2572
情歌与酒
情歌与酒 2020-12-02 09:29

I have an application that uses ASP.NET Forms Authentication. For the most part, it\'s working great, but I\'m trying to add support for a simple API via an .ashx file. I wa

12条回答
  •  被撕碎了的回忆
    2020-12-02 10:31

    After a bit of investigation, it looks like the FormsAuthenticationModule adds a handler for the HttpApplicationContext.EndRequest event. In it's handler, it checks for a 401 status code and basically does a Response.Redirect(loginUrl) instead. As far as I can tell, there's no way to override this behaviour if want to use FormsAuthenticationModule.

    The way I ended up getting around it was by disabling the FormsAuthenticationModule in the web.config like so:

    
    

    And then implementing the Application_AuthenticateEvent myself:

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

    It's actually slightly more complicated than that, but I basically got the code by looking at the implementation of FormsAuthenticationModule in reflector. My implementation is different to the built-in FormsAuthenticationModule in that it doesn't do anything if you respond with a 401 - no redirecting to the login page at all. I guess if that ever becomes a requirement, I can put an item in the context to disable the auto-redirect or something.

提交回复
热议问题