1、介绍
WCF支持多种认证技术,例如Windowns认证、X509证书、Issued Tokens、用户名密码认证等,在跨Windows域分布的系统中,用户名密码认证还是比较常用的,要实现用户名密码认证,就必须需要X509证书,为什么呢?因为我们需要X509证书这种非对称密钥技术来实现WCF在Message传递过程中的加密和解密,要不然用户名和密码就得在网络上明文传递!详细说明就是客户端把用户名和密码用公钥加密后传递给服务器端,服务器端再用自己的私钥来解密,然后传递给相应的验证程序来实现身份验证。
2、生成X509证书
当然,做个测试程序就没有必要去申请一个X509数字签名证书了,微软提供了一个makecert.exe的命令专门用来生成测试使用的X509证书的,那我们就来建立一个测试用的证书,在cmd下输入以下命令:
makecert.exe -sr LocalMachine -ss My -a sha1 -n CN=MyServerCert -sky exchange –pe
这个命令的意思就是创建一个测试的X509证书,这个证书放在存储位置为'Localmachine'的'My'这个文件夹下,证书主题名字叫'MyServerCert'
3、服务端代码
3.1、WCF项目上添加引用'System.IdentityModel'
3.2、建立一个新的类文件并继承自'System.IdentityModel.Selectors.UserNamePasswordValidator',然后我们重写里面的'Validate'方法来实现用户名密码认证逻辑。
3.3、代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
namespace ServerWcfService.CustomValidators
{
public class MyCustomValidator : UserNamePasswordValidator
{
/// <summary>
/// Validates the user name and password combination.
/// </summary>
/// <param name="userName">The user name.</param>
/// <param name="password">The password.</param>
public override void Validate(string userName, string password)
{
// validate arguments
if (string.IsNullOrEmpty(userName))
throw new ArgumentNullException("userName");
if (string.IsNullOrEmpty(password))
throw new ArgumentNullException("password");
// check if the user is not xiaozhuang
if (userName != "xiaozhuang" || password != "123456")
throw new SecurityTokenException("用户名或者密码错误!");
}
}
}
3.4、WEB.CONFIG文件配置
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="mySecureBinding">
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="ServerWcfService.Services.MySimpleServiceBehavior"name="ServerWcfService.Services.MySimpleService">
<endpoint address="" binding="wsHttpBinding"contract="ServerWcfService.ServiceContracts.IMySimpleService" bindingConfiguration="mySecureBinding">
<identity>
<dns value="MyServerCert"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServerWcfService.Services.MySimpleServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceCredentials>
<serviceCertificate findValue="MyServerCert" x509FindType="FindBySubjectName"storeLocation="LocalMachine" storeName="My"/>
<userNameAuthentication userNamePasswordValidationMode="Custom"customUserNamePasswordValidatorType="ServerWcfService.CustomValidators.MyCustomValidator,ServerWcfService"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
4、客户端代码
protected void btnPrint_Click(object sender, EventArgs e)
{
TestWCFService.MySimpleServiceClient client = new ClientWeb.TestWCFService.MySimpleServiceClient();
client.ClientCredentials.UserName.UserName = "xiaozhuang";
client.ClientCredentials.UserName.Password = "123456";
lbMessage.Text = client.PrintMessage(txtMessage.Text);
}
如果你有一个真正的X509证书,那么现在的代码就可以正常运行了,但是测试需要进行如下的特殊处理4.1、引用System.IdentityModel
4.2、建立一个新的类文件并继承自'System.IdentityModel.Selectors.X509CertificateValidator',然后我们重写里面的'Validate'方法来实现我们自己的X509认证逻辑
using System;
using System.Configuration;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Security.Cryptography.X509Certificates;
namespace ClientWeb.CustomX509Validator
{
/// <summary>
/// Implements the validator for X509 certificates.
/// </summary>
public class MyX509Validator: X509CertificateValidator
{
/// <summary>
/// Validates a certificate.
/// </summary>
/// <param name="certificate">The certificate the validate.</param>
public override void Validate(X509Certificate2 certificate)
{
// validate argument
if (certificate == null)
throw new ArgumentNullException("X509认证证书为空!");
// check if the name of the certifcate matches
if (certificate.SubjectName.Name != ConfigurationManager.AppSettings["CertName"])
throw new SecurityTokenValidationException("Certificated was not issued by thrusted issuer");
}
}
}
4.3、配置文件
<behaviors>
<endpointBehaviors>
<behavior name="myClientBehavior">
<clientCredentials>
<serviceCertificate>
<authentication certificateValidationMode="Custom"customCertificateValidatorType="ClientWeb.CustomX509Validator.MyX509Validator,ClientWeb" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>