X.509 比较适合验证 "客户机" 的身份,而另外一方面,我们可能需要针对具体的 "用户" 进行验证。本文将记述基于 "用户名/密码" 方式的身份验证开发步骤。
1. 服务器数字证书
我们同样需要为服务器准备一个数字证书。
D:/>makecert -r -pe -n "CN=MyServer" -ss My -sky exchange
2. 创建服务
[ServiceContract]
public interface IService
{
[OperationContract]
string Test();
}
public class MyService : IService
{
public string Test()
{
return "Server:" + DateTime.Now.ToString();
}
}
public class WcfTest
{
public static void Test()
{
ServiceHost host = new ServiceHost(typeof(MyService));
host.Open();
}
}
public interface IService
{
[OperationContract]
string Test();
}
public class MyService : IService
{
public string Test()
{
return "Server:" + DateTime.Now.ToString();
}
}
public class WcfTest
{
public static void Test()
{
ServiceHost host = new ServiceHost(typeof(MyService));
host.Open();
}
}
我们通过继承 UserNamePasswordValidator 来创建一个自定义验证器。
public class MyUserNamePasswordValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (userName != "user" || password != "pwd")
{
throw new SecurityTokenException("Unknown Username or Password");
}
}
}
{
public override void Validate(string userName, string password)
{
if (userName != "user" || password != "pwd")
{
throw new SecurityTokenException("Unknown Username or Password");
}
}
}
接 下来创建服务器配置文件,我们使用 WsHttpBinding,采取 Message 安全模式。其他的设置还包括 certificateValidationMode、userNamePasswordValidationMode、 customUserNamePasswordValidatorType 等。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="NewBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode="None" />
</clientCertificate>
<serviceCertificate findValue="MyServer" storeLocation="CurrentUser"
x509FindType="FindBySubjectName" />
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType=
"Learn.Library.WCF.MyUserNamePasswordValidator, Learn.Library" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="Binding1">
<security mode="Message">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="NewBehavior" name="Learn.Library.WCF.MyService">
<endpoint address="service" binding="wsHttpBinding" bindingConfiguration="Binding1"
name="username" contract="Learn.Library.WCF.IService" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="NewBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode="None" />
</clientCertificate>
<serviceCertificate findValue="MyServer" storeLocation="CurrentUser"
x509FindType="FindBySubjectName" />
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType=
"Learn.Library.WCF.MyUserNamePasswordValidator, Learn.Library" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="Binding1">
<security mode="Message">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="NewBehavior" name="Learn.Library.WCF.MyService">
<endpoint address="service" binding="wsHttpBinding" bindingConfiguration="Binding1"
name="username" contract="Learn.Library.WCF.IService" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
3. 创建客户端
启动服务器后,创建客户端代理文件。注意自动生成的客户端配置文件中包含了服务器数字证书的相关信息。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="username">
<security mode="Message">
<message clientCredentialType="UserName" negotiateServiceCredential="true"
algorithmSuite="Default" establishSecurityContext="true" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8080/service" binding="wsHttpBinding"
bindingConfiguration="username" contract="ConsoleApplication1.localhost.IService"
name="username">
<identity>
<certificate encodedValue="AwAA...IgFHqYA==" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="username">
<security mode="Message">
<message clientCredentialType="UserName" negotiateServiceCredential="true"
algorithmSuite="Default" establishSecurityContext="true" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8080/service" binding="wsHttpBinding"
bindingConfiguration="username" contract="ConsoleApplication1.localhost.IService"
name="username">
<identity>
<certificate encodedValue="AwAA...IgFHqYA==" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
开始调用服务,注意将 CertificateValidationMode 设置为 None,当然也可以写到配置文件中。
using (ServiceClient client = new ServiceClient())
{
client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode =
X509CertificateValidationMode.None;
client.ClientCredentials.UserName.UserName = "user";
client.ClientCredentials.UserName.Password = "pwd";
Console.WriteLine(client.Test());
}
{
client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode =
X509CertificateValidationMode.None;
client.ClientCredentials.UserName.UserName = "user";
client.ClientCredentials.UserName.Password = "pwd";
Console.WriteLine(client.Test());
}
OK! 测试通过!