在没有IIS的环境中,如何创建一个安全的WCF服务,并使用Channel Factory进行调用,是一个常见的问题。本文将介绍如何使用Visual Studio 2010 Express创建一个完全自托管的WCF服务,并通过自定义验证进行安全调用。
最近,遇到了一个需要在没有IIS的环境中创建一个安全的WCF服务的挑战。只有Visual Studio 2010 Express,并且需要创建一个完全独立的服务,不依赖于任何证书。
解决方案是创建一个自托管的WCF服务,使用自定义验证,并使用Channel Factory进行调用。在创建这个POC的过程中,遇到了一些错误,于是开始在网上搜索类似的POC。找到了一些,但大多数都不是可以直接运行的,要么只有服务器端代码,要么只有客户端代码,或者需要在app.config中进行一些更改。
将创建一个自托管的WCF服务,使用传输安全(用户名/密码)进行保护。
using System;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.IdentityModel.Tokens;
using System.IdentityModel.Selectors;
using System.Security.Principal;
using System.ServiceModel.Description;
namespace SecurWCFSelfHosting
{
class Program
{
[ServiceContract]
public interface IDemoService
{
[OperationContract]
int Add(int x, int y);
}
public class DemoService : IDemoService
{
public int Add(int x, int y)
{
return x + y;
}
}
static void Main(string[] args)
{
Uri httpUrl = new Uri("http://localhost:999/MyService/");
ServiceHost host = new ServiceHost(typeof(DemoService), httpUrl);
BasicHttpBinding http = new BasicHttpBinding();
http.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
http.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
host.AddServiceEndpoint(typeof(IDemoService), http, "");
host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new MyCustomValidator();
ServiceMetadataBehavior smb = host.Description.Behaviors.Find();
if (smb == null)
{
smb = new ServiceMetadataBehavior { HttpGetEnabled = true };
host.Description.Behaviors.Add(smb);
}
host.Open();
Console.WriteLine(DateTime.Now.ToString() + " Service is host at " + httpUrl.ToString());
Console.WriteLine("The service is running in the following account: " + WindowsIdentity.GetCurrent().Name);
Console.WriteLine("Press to terminate service.");
Console.ReadLine();
}
}
public class MyCustomValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (userName != "h" || password != "p")
{
throw new SecurityTokenException("Validation Failed!");
}
Console.WriteLine(DateTime.Now.ToString() + " Validation success for user :" + userName);
}
}
}
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
namespace WCFConsumeByChannelFactory
{
class Program
{
static void Main(string[] args)
{
EndpointAddress Serviceaddress = new EndpointAddress("http://localhost:999/MyService/");
BasicHttpBinding httpBinding = new BasicHttpBinding();
httpBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
ChannelFactory myChannelFactory = new ChannelFactory(httpBinding, Serviceaddress);
var defaultCredentials = myChannelFactory.Endpoint.Behaviors.Find();
myChannelFactory.Credentials.UserName.UserName = "h";
myChannelFactory.Credentials.UserName.Password = "p";
IDemoService wcfClient1 = myChannelFactory.CreateChannel();
double s = wcfClient1.Add(73, 22);
Console.WriteLine(s.ToString());
((IClientChannel)wcfClient1).Close();
Console.ReadKey();
}
}
}