我们有一个WCF服务(ServiceContact + Implementation类)。该服务最初是使用ServiceHost和BasicHttpBinding通过TransportWithMessageCredential自托管的。
已将客户端配置为使用此服务,除非指向新的URL,否则无法更改。
现在,我们要更改服务的托管方式。新主机(不是IIS)上的入口点是Http请求消息(标头和正文)。这个想法是,我们将客户端指向新主机,并弥合Http请求和WCF堆栈之间的差距。
理论上,这听起来像我们必须创建一个新的TransportBindingElement,然后创建一个CustomBinding才能使用此传输。事实证明,这令人难以置信。
或者,听起来更容易的是,如果可能的话,从http请求创建WCF消息实例,然后以某种方式通过BasicHttpBinding使用的各种现有绑定元素将其发送(主要是处理所有Security标头),以及某种方式最终进入我们的WCF服务实施。
有人对是否可能以及如何实现有任何想法吗?
2019-06-14:添加示例以演示挑战:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace ConsoleApp5
{
[ServiceContract]
public interface IService
{
[OperationContract]
string Invert(string s);
}
public interface IServiceChannel : IService, IClientChannel
{
}
public class Service : IService
{
public string Invert(string s)
{
char[] charArray = s.ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}
}
class Program
{
static void Main(string[] args)
{
ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
Console.WriteLine("Enter 1 for WCF Listene, anything else for HttpListener");
var option = Console.ReadLine();
if(option == "1")
{
Task.Run(() => StartWCFHost());
}
else
{
Task.Run(() => StartHttpListenerHost());
}
Console.WriteLine("hosting");
Console.WriteLine("Enter string to invert");
var streingToInvert = Console.ReadLine();
var binding = new BasicHttpsBinding();
binding.Security.Mode = BasicHttpsSecurityMode.TransportWithMessageCredential;
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.Certificate;
var cf = new ChannelFactory<IService>(binding, new EndpointAddress("https://localhost:20443/myservice"));
cf.Credentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "dc72486676c5e983589897fca5051a360376777d");
var client = cf.CreateChannel();
try
{
var r = client.Invert(streingToInvert);
Console.WriteLine(r);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
private static void StartHttpListenerHost()
{
var web = new HttpListener();
web.Prefixes.Add("https://localhost:20443/");
Console.WriteLine("Listening..");
web.Start();
while(true)
{
var context = web.GetContext();
var soapMessage = Message.CreateMessage(XmlReader.Create(context.Request.InputStream), int.MaxValue, MessageVersion.Soap11);
//TODO: How do we process request security related headers and give a proper response to client?
//Ideally we want to use Service implementation class and WCF Bindings/BindingElements to process headers and generate response message
//For the time being just read body write it to console
var body = soapMessage.GetReaderAtBodyContents().ReadOuterXml();
Console.WriteLine(soapMessage.ToString());
Console.WriteLine(body);
context.Response.Close();
}
}
private static void StartWCFHost()
{
ServiceHost sh = new ServiceHost(typeof(Service), new Uri("https://localhost:20443/myservice"));
var binding = new BasicHttpsBinding();
binding.Security.Mode = BasicHttpsSecurityMode.TransportWithMessageCredential;
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.Certificate;
var se = sh.AddServiceEndpoint(typeof(IService), binding, "https://localhost:20443/myservice");
sh.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpsGetEnabled = true });
sh.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "dc72486676c5e983589897fca5051a360376777d");
sh.Credentials.ClientCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None;
sh.Open();
}
}
}