简体   繁体   中英

Custom authorize attribute doesn't work after deploying to IIS

I have overridden the HandleUnauthorizedRequest method in my asp.net mvc application to ensure it sends a 401 response to unauthorized ajax calls instead of redirecting to login page. This works perfectly fine when I run it locally, but my overridden method doesn't get called once I deploy to IIS. The debug point doesn't hit my method at all and straight away gets redirected to the login page.

This is my code:

public class AjaxAuthorizeAttribute : AuthorizeAttribute
    {
        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                filterContext.HttpContext.Response.Clear();
                filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;

                filterContext.Result = new JsonResult
                {
                    Data = new
                    {
                        success = false,
                        resultMessage = "Errors"
                    },
                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
                };
                filterContext.HttpContext.Response.End();
                base.HandleUnauthorizedRequest(filterContext);
            }
            else
            {
                var url = HttpContext.Current.Request.Url.AbsoluteUri;
                url = HttpUtility.UrlEncode(url);
                filterContext.Result = new RedirectResult(ConfigurationManager.AppSettings["LoginUrl"] + "?ReturnUrl=" + url);
            }
        }
    }

and I have the attribute [AjaxAuthorize] declared on top of my controller. What could be different once it's deployed to IIS?

Update: Here's how I'm testing, it's very simple, doesn't even matter whether it's an ajax request or a simple page refresh after the login session has expired -

  1. I deploy the site onto my local IIS
  2. Login to the website, go to the home page - "/Home"
  3. Right click on the "Logout" link, "Open in a new tab" - This ensures that the home page is still open on the current tab while the session is logged out.
  4. Refresh Home page. Now here, the debug point should hit my overridden HandleUnauthorizedRequest method and go through the if/else condition and then redirect me to login page. But it doesn't! it just simply redirects to login page straight away. I'm thinking it's not even considering my custom authorize attribute.

When I run the site from visual studio however, everything works fine, the control enters the debug point in my overridden method and goes through the if/else condition.

When you deploy your web site to IIS, it will run under IIS integrated mode by default. This is usually the best option. But it also means that the HTTP request/response model isn't completely initialized during the authorization check. I suspect this is causing IsAjaxRequest() to always return false when your application is hosted on IIS.

Also, the default HandleUnauthorizedRequest implementation looks like this:

    protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        // Returns HTTP 401 - see comment in HttpUnauthorizedResult.cs.
        filterContext.Result = new HttpUnauthorizedResult();
    }

Effectively, by calling base.HandleUnauthorizedRequest(context) you are overwriting the JsonResult instance that you are setting with the default HttpUnauthorizedResult instance.

There is a reason why these are called filters . They are meant for filtering requests that go into a piece of logic, not for actually executing that piece of logic. The handler ( ActionResult derived class) is supposed to do the work.

To accomplish this, you need to build a separate handler so the logic that the filter executes waits until after HttpContext is fully initialized.

public class AjaxAuthorizeAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        filterContext.Result = new AjaxHandler();
    }
}

public class AjaxHandler : JsonResult
{
    public override void ExecuteResult(ControllerContext context)
    {
        var httpContext = context.HttpContext;
        var request = httpContext.Request;
        var response = httpContext.Response;
        if (request.IsAjaxRequest())
        {
            response.StatusCode = (int)HttpStatusCode.Unauthorized;

            this.Data = new
            {
                success = false,
                resultMessage = "Errors"
            };
            this.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
            base.ExecuteResult(context);
        }
        else
        {
            var url = request.Url.AbsoluteUri;
            url = HttpUtility.UrlEncode(url);
            url = ConfigurationManager.AppSettings["LoginUrl"] + "?ReturnUrl=" + url;
            var redirectResult = new RedirectResult(url);
            redirectResult.ExecuteResult(context);
        }
    }
}

NOTE: The above code is untested. But this should get you moving in the right direction.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM