Authentication to a WCF service not maintained usi

2019-07-10 07:55发布

问题:

I've setup a site with forms authentication and AspNetCompatibility enabled. The actual client is a silverlight application, and works fine, however I want to unit test the application separately using normal .net to exercise the service (live system for methods without side-effects). However, when I stop using Silverlight and start using full .net it doesn't remain authenticated

In a web service I have:

[OperationContract]
public bool Login(string Username, string Password, bool isPersistent)
{
    if (Membership.ValidateUser(Username, Password))
    {
        FormsAuthentication.SetAuthCookie(Username, isPersistent);
        return true;
    }
    else
    {
        return false;
    }
}

[OperationContract]
public bool IsLoggedIn()
{
    return HttpContext.Current.User.Identity.IsAuthenticated;
}

Then in a test method on the client I call it like so:

Assert.IsTrue(Client.Login("MyUsername","MyPassword", true));
Assert.IsTrue(Client.IsLoggedIn());

The client is an instance of the automatically generated ServiceReference client for .net. The first assertion passes but the second one fails, i.e. from one method call to the next it stops being logged in. A similar method in the silverlight application would pass.

How can I make normal .net behave correctly as silverlight would? Is there just a better way of configuring client/service for full .net?

Aditional Info Requested

Service Config:

<services>
  <service behaviorConfiguration="MyBehaviour" name="SSCCMembership.Web.Services.LoginService">
    <endpoint address="" binding="customBinding" bindingConfiguration="customBindingBinary"
      contract="SSCCMembership.Web.Services.LoginService" />
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
  </service>
</services>
<bindings>
  <customBinding>
    <binding name="customBindingBinary">
      <binaryMessageEncoding />
      <httpTransport />
    </binding>
  </customBinding>
</bindings>
<behaviors>
  <serviceBehaviors>
    <behavior name="MyBehaviour">
      <serviceMetadata httpGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="true" />
    </behavior>
  </serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />

Authentication Config:

<authentication mode="Forms" />
<membership defaultProvider="OdbcProvider">
  <providers>
    <clear />
    <add name="OdbcProvider" type="SSCCMembership.Web.SimpleMembershipProvider" applicationName="/SSCCMembership" requiresUniqueEmail="false" connectionStringName="mainConn" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="true" writeExceptionsToEventLog="true" />
  </providers>
</membership>

I then use the following to construct the client in WPF

public static T LoadService<T>(string URI, Func<CustomBinding, EndpointAddress, T> F)
{
    try
    {
        Uri U = new Uri(new Uri(Root), URI);

        BinaryMessageEncodingBindingElement binary = new BinaryMessageEncodingBindingElement();
        HttpTransportBindingElement transport;
        if (U.Scheme == "http")
            transport = new HttpTransportBindingElement();
        else if (U.Scheme == "https")
            transport = new HttpsTransportBindingElement();
        else
            throw new Exception(U.Scheme + " is not a recognised URI scheme");
        transport.MaxBufferSize = int.MaxValue;
        transport.MaxReceivedMessageSize = transport.MaxBufferSize;
        transport.AllowCookies = true;

        CustomBinding binding;
        binding = new CustomBinding(binary, transport);


        EndpointAddress address = new EndpointAddress(U);

        return F(binding, address);
    }
    catch (Exception)
    {
        return default(T);
    }
}

Which I call like:

var Client = LoadService(ServiceLocation, (b,e)=>new LoginServiceClient(b,e));

回答1:

I think the main problem you are having is due to the context that each application runs in.

A Silverlight application runs in the context of the web-page it's hosted in - and thus, can maintain session info, cookies, and all sorts of other things that you don't need to manually worry about.

As soon as you connect with an application running outside of the browser, your context (as far as asp.net is concerned) has changed. Have you tried enabling/installing the silverlight application out-of-browser to see if you have the same errors?

One way to work around this is to use the Silverlight Unit Test project and connect that to your web service for testing. (I have successfully done this with enterprise silverlight apps for the company I work for) The silverlight unit test project results in a silverlight app that can be hosted on a web page, and run from there - in the same context as any other silverlight app, as far as ASP.net is concerned, making your web-service calls much less likely to fail due to context.

Check out the Silverlight Unit Test Famework Homepage for more info...



回答2:

Change your IsLoggedIn() method to reference the ServiceSecurityContext instead of HttpContext:

[OperationContract]
public bool IsLoggedIn()
{
    return ServiceSecurityContext.Current.PrimaryIdentity.IsAuthenticated;
}


回答3:

I had this same type of problem and ended up just implementing the WCF Authentication Services... The documentation was straight forward and it works great with the ASP.NET forms auth provider.
http://msdn.microsoft.com/en-us/library/bb386582.aspx