在构建基于Windows Communication Foundation (WCF) 的应用程序时,安全性是一个不可忽视的重要方面。本文将指导如何创建一个使用自定义用户名和密码认证的安全WCF服务,并通过证书配置确保服务的安全性。
WCF提供了丰富的安全特性,包括传输层消息安全和传输消息安全。每种安全类型都有其优势和开销。应用程序有许多不同的客户端需要连接到服务,并且它们需要从数据库中进行身份验证,因此最佳解决方案是使用自定义用户名-密码认证的消息级安全。通过网上的资料和一些努力,实现了一个具体的解决方案,希望对其他人有所帮助。
解决方案使用Visual Studio 2010创建,包含三个项目:WCF服务、网站和桌面应用程序(客户端应用程序)。
WCF服务包含一个函数GetServertime(),代码如下:
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetServertime();
}
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class Service1 : IService1
{
public string GetServertime()
{
return DateTime.Now.ToString();
}
}
创建一个类UserNamePassValidator,并实现以下代码:
using System;
using System.ServiceModel;
namespace CustomUsernamePasswordAuth.Service
{
class UserNamePassValidator : System.IdentityModel.Selectors.UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (userName == null || password == null)
{
throw new ArgumentNullException();
}
if (!(userName == "fayaz" && password == "soomro"))
{
throw new FaultException("Incorrect Username or Password");
}
}
}
}
在Web应用程序中添加对服务的引用。添加一个文本文件并将其重命名为UserNamePassService.svc,然后添加以下代码:
<%@ ServiceHost Language="C#" Debug="true" Service="CustomUsernamePasswordAuth.Service.Service1" %>
修改web.config文件,并添加以下配置:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="Behavior1">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceCredentials>
<serviceCertificate findValue="MyWebSite" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName"/>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="CustomUsernamePasswordAuth.Service.UserNamePassValidator, CustomUsernamePasswordAuth.Service"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
按照以下示例设置绑定配置,命名为Binding1,并将安全模式设置为Message,clientCredentialType设置为UserName:
<bindings>
<wsHttpBinding>
<binding name="Binding1">
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
设置服务端点,包括wsHttp端点和Mex端点用于元数据交换。基本地址为http://localhost/。完整的服务地址将是http://localhost/UserNamePassService.svc。
<services>
<service behaviorConfiguration="Behavior1" name="CustomUsernamePasswordAuth.Service.Service1">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="Binding1" contract="CustomUsernamePasswordAuth.Service.IService1" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost/" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
打开IIS管理器。右键点击站点并添加网站。将其命名为WebSite,设置应用程序池为DefaultAppPool,并选择物理路径,将端口设置为83。
在解决方案资源管理器中右键点击网站项目并发布。选择发布方法为文件系统,目标位置为http://localhost:83。
下载文章开头提供的Pluralsight SelfCert。以管理员身份运行该工具;否则,它将崩溃。
客户端应用程序是桌面应用程序,仅包含地址文本框和获取服务器时间的按钮。现在向项目添加服务引用:
private void button1_Click(object sender, EventArgs e)
{
string time = "";
// 方法1:使用配置文件创建客户端
Service1Client c = new Service1Client();
c.ClientCredentials.UserName.UserName = "fayaz";
c.ClientCredentials.UserName.Password = "soomro";
c.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
time = c.GetServertime();
MessageBox.Show(time);
// 方法2:通过编码创建端点和绑定
var ServiceendPoint = new EndpointAddress(new Uri(txtServiceAddress.Text), EndpointIdentity.CreateDnsIdentity("MyWebSite"));
var binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.Message;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
var result = new Service1Client(binding, ServiceendPoint);
result.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
result.ClientCredentials.UserName.UserName = "fayaz";
result.ClientCredentials.UserName.Password = "soomro";
time = result.GetServertime();
MessageBox.Show(time);
}