1

下面的代码假设运行自定义身份验证的需要向Silverlight 4客户端提供其服务的WCF服务(请参阅下面的代码)。自行托管的WCF +自定义UserNamePasswordValidator + Silverlight 4

结果是,即使clientaccesspolicy.xml在浏览器中可见并且不显示SSL错误,也会抛出臭名昭着的clientaccesspolicy安全错误通信异常未命中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)我们有CA在LOCAL MACHINE的MY容器中签署了SSL证书并使用了网络

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

以上执行成功,主机正确加载并允许创建一个新的silverlight项目。

3)Silverlight项目是与附加服务引用一个只是一个新项目silveright和以下代码:

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> 

回答

0

Blockquote 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是否能够在获取clientaccesspolicy时发送客户端证书,但是如果您的网页也访问该网站,浏览器应该使用您提供的证书。所以你可以尝试添加一个链接到你的托管html/aspx中的安全站点,这将要求你选择一个证书,然后SL将使用该证书