Prevent XmlHttpRequest redirect response in .Net MVC WS-Federation Site

霸气de小男生 提交于 2019-12-09 18:20:20

问题


I'm using WS Federated (Claims Aware) authentication on an MVC 3 site and am having trouble keeping some of my API controllers that send JSON from returning a redirect when the authentication fails. I have an Area called API with several controllers that just return JSON, these controllers all inherit from the same base class. I want to send down legitimate 401 error responses instead of 302 redirects that are happening by default.

I followed some directions I found for creating a custom WSFederationAuthenticationModule in concert with a filter I put on my API controller actions:

public class WSFederationServiceAuthenticationModule : WSFederationAuthenticationModule
{
    private static Log4NetLoggingService logger = new Log4NetLoggingService();

    public const string IsServiceIndicator = "ROIP.IsService";

    protected override void OnAuthorizationFailed(AuthorizationFailedEventArgs e)
    {
        base.OnAuthorizationFailed(e);            

        var isService = HttpContext.Current.Items[IsServiceIndicator];

        if (isService != null)
        {
            logger.Info("WSFedService: Found IsService");
            e.RedirectToIdentityProvider = false;
        }
        else
        {
            logger.Info("WSFedService: Did not find IsService");
        }
    }
}

public class WSFederationServiceAuthAttribute : ActionFilterAttribute
{
    private static Log4NetLoggingService logger = new Log4NetLoggingService();

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        base.OnActionExecuting(filterContext);

        // Set an item that indicates this is a service request, do not redirect.
        logger.Info("WSFedService: Setting IsService");
        HttpContext.Current.Items[WSFederationServiceAuthenticationModule.IsServiceIndicator] = 1;
    }
}

But my logging shows that I am never finding the IsService item in the Items:

{INFO}02/29 03:39:21 - WSFedService: Setting IsService
{INFO}02/29 03:39:32 - WSFedService: Setting IsService
{INFO}02/29 03:39:32 - WSFedService: Setting IsService
{INFO}02/29 03:50:39 - WSFedService: Did not find IsService
{INFO}02/29 03:53:16 - WSFedService: Did not find IsService
{INFO}02/29 03:53:29 - WSFedService: Did not find IsService

I think this may be a problem with the HttpContext.Current not being the same between the filter and the module, but I'm not sure.

Another option I tried was to subscribe to the FederatedAuthentication.WSFederationAuthenticationModule.RedirectingToIdentityProvider event in the Application_Start event of my Global.asax.cs, but the WSFederationAuthenticationModule is null at that time.

private void ConfigureWSFederationAuthentication()
{
    bool hasFederatedAuthentication = false;
    try
    {
        if (FederatedAuthentication.WSFederationAuthenticationModule != null)
        {
            hasFederatedAuthentication = true;
        }
    }
    catch
    {
        hasFederatedAuthentication = false;
    }

    if (hasFederatedAuthentication)
    {
        Logger.Info("WSFederation: Registering for Event Handler");
        FederatedAuthentication.WSFederationAuthenticationModule.RedirectingToIdentityProvider += (s, e) =>
            {
                var msg = string.Empty;
                try
                {
                    if (HttpContext.Current.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
                    {
                        e.Cancel = true;
                        msg = "Found XMLHttpRequest header";
                    }
                    else
                    {
                        msg = "Did not find XMLHttpRequest header";
                    }
                }
                catch (Exception ex)
                {
                    msg = "WSFederation: Event Handler Error: " + ex.Message;
                }

                Logger.Info("WSFederation: Redirecting from Event Handler: " + msg);
            };
    }
    else
    {
        Logger.Info("WSFederation: Null WSFederationAuthenticationModule");
    }
}

I'd like to know either how to get the first option working, or where I should subscribe to the RedirectingToIdentityProvider event.


回答1:


I think I've found an answer to this problem and want to circle back and leave an answer for anyone else in the world that might encounter this.

My problem was that the HttpContext.Current.Items wasn't matching up between my ActionFilterAttribute and the WSFederationAuthenticationModule so I ended up inspecting the context and adding some checks similar to Phil Haacks Forms Redirect Suppress Example

Here is what my updated custom WSFederationAuthenticationModule looks like:

public class WSFederationServiceAuthenticationModule : WSFederationAuthenticationModule
{
    private static Log4NetLoggingService logger = new Log4NetLoggingService();

    protected override void OnAuthorizationFailed(AuthorizationFailedEventArgs e)
    {
        base.OnAuthorizationFailed(e);            

        var context = HttpContext.Current;
        var req = context.Request;
        var resp = context.Response;

        if (req == null || resp == null)
        {
            logger.Info("WSFedService: Did not find Request or Response");
            return;
        }

        if ((resp.StatusCode == 302 || resp.StatusCode == 401) && req.Headers["X-Requested-With"] == "XMLHttpRequest")
        {
            logger.Info("WSFedService: Found Redirect and Header");
            e.RedirectToIdentityProvider = false;
        }
        else
        {
            logger.Info(string.Format("WSFedService: Did not find redirect status code or XMLHttpRequest Header: {0}", resp.StatusCode));
        }

    }
}

And of course, you'll need to add this to your web.config in place of the default authentication module:

<system.web>
    <httpModules>
        <!-- Old and Busted...
        <add name="WSFederationAuthenticationModule"
           type="Microsoft.IdentityModel.Web.WSFederationAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
        -->

        <!-- New Hotness... -->
        <add name="WSFederationAuthenticationModule"
           type="MyApp.Web.Authentication.WSFederationServiceAuthenticationModule, MyApp.Web" />
    </httpModules>
</system.web>

<system.webServer>
    <modules>
        <!-- Old and Busted...
        <add name="WSFederationAuthenticationModule"
           type="Microsoft.IdentityModel.Web.WSFederationAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
           preCondition="managedHandler"/>
        -->

        <!-- New Hotness... -->
        <add name="WSFederationAuthenticationModule"
           type="MyApp.Web.Authentication.WSFederationServiceAuthenticationModule, MyApp.Web"
           preCondition="managedHandler"/>

    </modules>
</system.webServer>



回答2:


I realize this thread is ancient, but I came across it while trying to solve the same problem (I have web apis that I want to use with active clients, and I want to send down a 401 if authentication goes south instead of redirecting.)

..And, depending on your situation, it might be easier/less involved to handle the authorization failed event in Global.asax and do your checking there.

This link is what I've been following: http://msdn.microsoft.com/en-us/library/system.identitymodel.services.wsfederationauthenticationmodule.authorizationfailed.aspx

..And it seems straightforward.




回答3:


In my case i have hosted my api's under path "/api" in website which are secured using JWT authentication.

For fix this issue, following overrides were done in WSFederationAuthenticationModule(ofcourse register this custom module in web.config, System.WebServer).

public class CustomWSFederationAuthenticationModule : System.IdentityModel.Services.WSFederationAuthenticationModule
{
    protected override void OnAuthorizationFailed(AuthorizationFailedEventArgs e)
    {
        if (!IsApiCall())
            e.RedirectToIdentityProvider = false;
        base.OnAuthorizationFailed(e);
    }

    protected override void OnEndRequest(object sender, EventArgs args)
    {
        if (!IsApiCall())
            base.OnEndRequest(sender, args);
    }

    private bool IsApiCall()
    {
        return (HttpContext.Current.Request.Path.ToLowerInvariant().Contains("/api/"));
    }
}


来源:https://stackoverflow.com/questions/9490811/prevent-xmlhttprequest-redirect-response-in-net-mvc-ws-federation-site

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!