简体   繁体   中英

WCF Service and AspNetCompatibilityEnabled=“true” causing Request Error

I have a WCF Service defined as:

[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class Service
{
    [OperationContract]
    [WebGet(ResponseFormat = WebMessageFormat.Json)]
    public string HelloWorld()
    {
        return "Hello World";
    }
}

My Web.Config file:

<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0"/>
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
        <endpointBehaviors>
            <behavior name="webHttpBehavior">
                <webHttp />
            </behavior>
        </endpointBehaviors>
    </behaviors>
    <bindings>
      <webHttpBinding>
        <binding name="webHttpBindingWithJsonP" crossDomainScriptAccessEnabled="true"/>
      </webHttpBinding>
    </bindings>
    <services>
      <service name="Service">
        <endpoint address="" binding="webHttpBinding" bindingConfiguration="webHttpBindingWithJsonP" contract="Service" behaviorConfiguration="webHttpBehavior"/>
      </service>
    </services>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true"/>
  </system.serviceModel>
</configuration>

I want to be able to access ASP .Net session variables in my WCF service, and I want the WCF service to be returning JSONP data, however even with this simple service, browsing to ../Service.svc/HelloWorld I am getting a 400 Bad Request error.

Can someone point me in the right direction?

Looks like the combination of JSONP, ASP.NET Compatibility and an authenticated user is not supported per this Microsoft forum.

According to the moderator of the forum, you need to disable one of the three.

Probably not the answer you were hoping for, but the moderator's explanation is pretty good and offers a few suggestions.

Hope this helps. Good luck!

I realise this has already been answered, but it's possible (though I'm unsure if recommended from a security perspective) to 'de-authenticate' a request early enough to pass the check being made by the webHttpBinding.

The gist is to set HttpContext.Current.User to be a new GenericPrincipal built on a GenericIdentity with no name or type mimicking what you'd see if an unauthenticated user had just hit your service - by the time the webHttpBinding performs its 'no authenticated JSONP calls' check the request is taking place in the context of an unauthenticated user.

Note: I'm unsure if there are security implications of this - one off the top of my head is that if you have an authenticated user their session state will still be available to your service which may be a bad thing, depending on what you're doing.

You can do this in a couple of places

  • By hooking the Application.AuthenticateRequest event, filtering by request URL
  • With a custom WCF message inspector

Example message inspector and behavior element (same class, very much use at own risk):

using System;
using System.Security.Principal;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Threading;
using System.Web;

namespace MyNamespace
{
    public class ForceAnonymousEndpointBehavior : BehaviorExtensionElement, IDispatchMessageInspector, IEndpointBehavior
    {
        public override Type BehaviorType
        {
            get { return typeof(ForceAnonymousEndpointBehavior); }
        }

        protected override object CreateBehavior()
        {
            return new ForceAnonymousEndpointBehavior();
        }

        object IDispatchMessageInspector.AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            HttpContext.Current.User = Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity("", ""), null);
            return null;
        }

        void IDispatchMessageInspector.BeforeSendReply(ref Message reply, object correlationState)
        {

        }

        void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new ForceAnonymousEndpointBehavior());
        }

        void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {

        }

        void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {

        }

        void IEndpointBehavior.Validate(ServiceEndpoint endpoint)
        {

        }
    }
}

Then in web.config register the behavior extension (in the system.serviceModel element):

<extensions>
  <behaviorExtensions>
    <add name="ForceAnonymous" type="MyNamespace.ForceAnonymousEndpointBehavior, MyAssembly" />
  </behaviorExtensions>
</extensions>

Adding the behavior to the endpointBehavior in question (again under system.serviceModel):

<behaviors>
  <endpointBehaviors>
    <behavior name="jsonpBehavior">
      <ForceAnonymous />
    </behavior>
  </endpointBehaviors>
</behaviors>

...and making sure the endpoint behavior is called out in your service's endpoint declaration by setting the behaviorConfiguration attribute to match the behavior name you used above.

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