Самостоятельно размещенный WCF + Пользовательский UserNamePasswordValidator + Silverlight 4

В приведенном ниже коде предполагается запуск собственной службы WCF с настраиваемой проверкой подлинности, которая должна предоставлять свои услуги клиенту Silverlight 4 (см. код ниже).

В результате выдается печально известное исключение связи clientaccesspolicy Security Error, даже несмотря на то, что clientaccesspolicy.xml виден в браузере и не показывает ошибки SSL. Точка останова clientaccesspolicy.xml не срабатывает.

Я понимаю, что мне нужно только указать запись, но я пробовал различные игры с clientaccesspolicy.xml, которые не работали.

Ваша помощь приветствуется

1) Это app.config и код службы:

    <?xml version="1.0"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
  <system.serviceModel>
    <client />
    <bindings>
      <basicHttpBinding>
        <binding name="slBindingWithUserNamePassValidator">
          <security mode="TransportWithMessageCredential">
            <message clientCredentialType="UserName" />
          </security>
        </binding>
      </basicHttpBinding>
      <webHttpBinding>
        <binding name="capService" crossDomainScriptAccessEnabled="true">
          <security mode="Transport" />
        </binding>
        <binding name="capServiceNoSSL" crossDomainScriptAccessEnabled="true">
          <security mode="None" />
        </binding>
      </webHttpBinding>
    </bindings>
    <services>
      <service behaviorConfiguration="svcBehavior" name="WCF_Self_Hosted_UserName_Validator.Service1">
        <endpoint address="" binding="webHttpBinding" bindingConfiguration="capService" behaviorConfiguration="capServiceBehavior"
          contract="WCF_Self_Hosted_UserName_Validator.ICAPService" />
        <endpoint address="" binding="webHttpBinding" bindingConfiguration="capServiceNoSSL" behaviorConfiguration="capServiceBehavior"
          contract="WCF_Self_Hosted_UserName_Validator.ICAPService" />
        <endpoint address="MyCustomValidationService" binding="basicHttpBinding" bindingConfiguration="slBindingWithUserNamePassValidator"
          contract="WCF_Self_Hosted_UserName_Validator.IService1">
        </endpoint>
        <host>
          <baseAddresses>
            <add baseAddress="https://(somesite):9999/" />
            <add baseAddress="http://(somesite):9998/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="svcBehavior">
          <serviceMetadata httpsGetEnabled="true" httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="capServiceBehavior">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
</configuration>

Код для услуги:

Using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.IdentityModel.Selectors;
using System.IO;
using System.ServiceModel.Web;

namespace WCF_Self_Hosted_UserName_Validator
{
    class Program
    {
        static void Main(string[] args)
        {
            MyServiceHost host = new MyServiceHost(new Service1());
            host.Open();
            Console.WriteLine("Host open...");
            Console.ReadLine();
        }

    }
    public class MyServiceHost : ServiceHost
    {
        SecurityValidator _securityValidator = null;
        public MyServiceHost(IService1 svc) : base(svc)
        {
            Credentials.UserNameAuthentication.UserNamePasswordValidationMode = System.ServiceModel.Security.UserNamePasswordValidationMode.Custom;
            _securityValidator = new SecurityValidator();
            Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = _securityValidator;
            Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "my-fqdn-valid-cert.dot.something");
         }
    }
    public class SecurityValidator : UserNamePasswordValidator
    {
        public SecurityValidator()
        {
        }
        public override void Validate(string userName, string password)
        {
            try
            {
                if (userName != "1" && password != "1")
                    throw new FaultException("auth error");
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        string GetPrivateInfo();
    }
    [ServiceContract]
    public interface ICAPService
    {
        [OperationContract, WebGet(UriTemplate = "/clientaccesspolicy.xml")]
        Stream GetClientAccessPolicy();
    }
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class Service1 : IService1, ICAPService
    {
        public string GetPrivateInfo()
        {
            return "Some info " + DateTime.Now.ToShortTimeString();
        }
        public System.IO.Stream GetClientAccessPolicy()
        {
            WebOperationContext ctx = new WebOperationContext(OperationContext.Current);
            string txtCap = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
<access-policy>
    <cross-domain-access>
        <policy>
            <allow-from http-request-headers=""*"">
                <domain uri=""*""/>
                <domain uri=""http://*""/>
                <domain uri=""https://*""/>
            </allow-from>
            <grant-to>
                <resource include-subpaths=""true"" path=""/""/>
            </grant-to>
        </policy>
    </cross-domain-access>
</access-policy>";
            WebOperationContext.Current.OutgoingResponse.ContentType = "text/xml";
            MemoryStream response = new MemoryStream(Encoding.UTF8.GetBytes(txtCap));
            return response;
        }
    }
}

2) У нас есть SSL-сертификат, подписанный ЦС, в МОЕМ контейнере ЛОКАЛЬНОЙ МАШИНЫ и используется netsh.

    netsh http add sslcert ipport=0.0.0.0:9999 certhash=aabbcc_thumbprint
 appid={my_app_id_guid} clientcertnegotiation=enable

Вышеприведенное выполняется успешно, хост загружается правильно и позволяет создать новый проект Silverlight.

3) Проект silverlight — это просто новый проект silverlight с добавлением ссылки на службу и следующим кодом:

namespace SilverlightApplication1
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            ServiceReference1.Service1Client c = new ServiceReference1.Service1Client();
            c.ClientCredentials.UserName.UserName = "1";
            c.ClientCredentials.UserName.Password = "1";
            c.GetPrivateInfoCompleted += new EventHandler<ServiceReference1.GetPrivateInfoCompletedEventArgs>(c_GetPrivateInfoCompleted);
            c.GetPrivateInfoAsync();
        }

        void c_GetPrivateInfoCompleted(object sender, ServiceReference1.GetPrivateInfoCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                this.Content = new TextBlock() { Text = e.Result };
            }
            else
            {
                this.Content = new TextBlock() { Text = e.Error.GetBaseException().Message };
            }
        }
    }
}

4) Это сгенерированный ServiceReferences.ClientConfig

    <configuration>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_IService1" maxBufferSize="2147483647"
                    maxReceivedMessageSize="2147483647">
                    <security mode="TransportWithMessageCredential" />
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="https://(TheAddress)/MyCustomValidationService"
                binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1"
                contract="ServiceReference1.IService1" name="BasicHttpBinding_IService1" />
        </client>
    </system.serviceModel>
</configuration>

person user605484    schedule 06.02.2011    source источник


Ответы (1)


Блокировка netsh http add sslcert ipport=0.0.0.0:9999 certhash=aabbcc_thumbprint appid={my_app_id_guid} clientcertnegotiation=enable

Вы использовали netsh с флагом clientcertnegotiation, который означает, что серверу требуется сертификат клиента. Когда Silverlight вызывает clientaccesspolicy, он не отправляет сертификат клиента, и поэтому вы получаете исключение.

Если вам не нужен сертификат клиента, снимите этот флаг.

Я не уверен, может ли SL отправить сертификат клиента при получении политики доступа к клиенту, но если ваша веб-страница также обращается к этому сайту, браузер должен использовать сертификат, который вы ему предоставили. Итак, вы можете попробовать добавить ссылку на защищенный сайт в свой хостинг html/aspx, что потребует от вас выбора сертификата, а затем SL будет использовать этот сертификат.

person IdoFlatow    schedule 07.02.2011