WCF支持多种认证技术,例如Windowns认证、X509证书、Issued Tokens、用户名密码认证等,在跨Windows域分布的系统中,用户名密码认证还是比较常用的,要实现用户名密码认证,就必须需要X509证书,为什么呢?因为我们需要X509证书这种非对称密钥技术来实现WCF在Message传递过程中的加密和解密,要不然用户名和密码就得在网络上明文传递!详细说明就是客户端把用户名和密码用公钥加密后传递给服务器端,服务器端再用自己的私钥来解密,然后传递给相应的验证程序来实现身份验证。
在网上搜了很多WCF认证相关文章,才最终把X.509用户名密码认证搞定,现在把自己的理解和经验总结如下:服务器环境:1、windows server 2003 sp2 2、IIS6.0
客户端环境:1、winXP, win7
开发环境:VS2010,HttpAnalyzer V5
开发步骤:
1、用VS2010创建WCF服务应用程序,如下图所示。
WCF模板已经有默认的服务,在此不需另外编码就行。再次,首先我们要编写自己的用户名密码认证逻辑,先要在WCF项目上添加引用'System.IdentityModel'然后我们建立一个新的类文件并继承自'System.IdentityModel.Selectors.UserNamePasswordValidator',然后我们重写里面的'Validate'方法来实现用户名密码认证逻辑。代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.IdentityModel; using System.IdentityModel.Selectors; using System.IdentityModel.Tokens; namespace WCFServerOne { public class MyCustomValidator : UserNamePasswordValidator { public override void Validate(string userName, string password) { if (userName != "lyx" || password != "123") { throw new SecurityTokenException("用户名或密码错误!"); } } } }上面只是一个简单的验证,实际应用中用户名和密码一般都保存在数据库中,如果验证不通过就抛出一个'SecurityTokenException'类型的异常;下一步我们需要配置一下服务端的webConfig文件,在修改配置文件之前我们先在win2003 IIS6.0上生成x.509证书,命令是VS2010 Visual Studio Tools自带的makecert.exe,一般2003服务器上没有,你可直接拷贝过去。命令格式(在cmd窗口输入):
makecert.exe -sr LocalMachine -ss My -a sha1 -n CN=MyServerCert -sky exchange –pe
1、需要注意的是需要查看win2003是否安装证书组件,如果没有安装,需要在控件面板添加删除程序中,安装win2003证书组件。在安装的过程中有可能需要安装CD。SP2的需要SP2 安装CD。
2、执行以上命令之后,在证书管理中就可以查看名为MyServerCert的证书了。接下证书就不需要去管它了(IIS也不需Https,我们只用X.509加密就行),我们来修改上面WCF服务的Web.Config文件,如下:
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.web> <compilation debug="true" targetFramework="4.0" /> </system.web> <system.serviceModel> <bindings> <wsHttpBinding> <binding name="mySecureBinding"> <security mode="Message"> <message clientCredentialType="UserName"/> </security> </binding> </wsHttpBinding> </bindings> <services> <service behaviorConfiguration="MySimpleServiceBehavior" name="WCFServerOne.MyServiceOne"> <endpoint address="" binding="wsHttpBinding" bindingConfiguration="mySecureBinding" contract="WCFServerOne.IServiceOne"> <identity> <dns value="MyServerCert" /> </identity> </endpoint> </service> </services> <behaviors> <serviceBehaviors> <behavior name="MySimpleServiceBehavior"> <!-- 为避免泄漏元数据信息,请在部署前将以下值设置为 false 并删除上面的元数据终结点 --> <serviceMetadata httpGetEnabled="true"/> <!-- 要接收故障异常详细信息以进行调试,请将以下值设置为 true。在部署前设置为 false 以避免泄漏异常信息 --> <serviceDebug includeExceptionDetailInFaults="false"/> <serviceCredentials> <serviceCertificate findValue="MyServerCert" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My"/> <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WCFServerOne.MyCustomValidator,WCFServerOne"/> </serviceCredentials> </behavior> </serviceBehaviors> </behaviors> <serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> </system.serviceModel> <system.webServer> <modules runAllManagedModulesForAllRequests="true"/> </system.webServer> </configuration>
security mode="Message"这个需要配置成Message
endpoint 需要注意的上 contract 写的是接口,behaviorConfiguration="myClientBehavior"在下面定义,名字一定不能错,不然自定验证就不会生效,如上所示。
ServiceCertificate节中指定了我们的X509证书的位置,以用来加解密message,usernameAuthentication节中指定了我们自己的用户名密码验证逻辑。
1、用VS2010创建WCF服务应用程序,如下图所示。
在项目添加服务引用,服务引用是IIS发布的地址,如:http://118.186.240.182:8090/MyServiceOne.svc 就会像WebService一样生成一个代理类,调用形式也是一样如:
private void button1_Click(object sender, EventArgs e) { MyWCFOne.ServiceOneClient client = new MyWCFOne.ServiceOneClient(); client.ClientCredentials.UserName.UserName = "lyx"; client.ClientCredentials.UserName.Password = "123"; textBox1.Text = client.GetData(10); }
如果你有一个真正的X509证书,那么现在的代码就可以正常运行了。但是很不幸,我们的证书是测试用的,我们运行的时候出错:'X.509 certificate CN=MyServerCert 链生成失败。所使用的证书具有无法验证的信任链。请替换该证书或更改 certificateValidationMode。已处理证书链,但是在不受信任提供程序信任的根证书中终止',WCF无法验证测试证书的信任链,那我们要做的就是绕过这个信任验证,具体做法如下:
先要在Asp.net Web应用程序项目上添加引用'System.IdentityModel'然后我们建立一个新的类文件并继承自'System.IdentityModel.Selectors.X509CertificateValidator',然后我们重写里面的'Validate'方法来实现我们自己的X509认证逻辑,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IdentityModel.Selectors; using System.IdentityModel.Tokens; using System.Security.Cryptography.X509Certificates; namespace WCFClient { class MyX509Validator : X509CertificateValidator { public override void Validate(X509Certificate2 certificate) { if(certificate == null) throw new ArgumentNullException("X509认证证书为空"); } } }你可以把Validate方法里面留空让所有的认证都通过,也可以自己定义认证逻辑,如果认证失败,就抛出' SecurityTokenValidationException'的异常,然后我们配置一下客户端的webconfig让它使用我们自己的X509认证,增加以下的配置节,并在' endpoint'节中指定 behaviorConfiguration="myClientBehavior"。
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <bindings> <wsHttpBinding> <binding name="WSHttpBinding_IServiceOne" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false"> <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" /> <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" /> <security mode="Message"> <transport clientCredentialType="Windows" proxyCredentialType="None" realm="" /> <message clientCredentialType="UserName" negotiateServiceCredential="true" algorithmSuite="Default" /> </security> </binding> </wsHttpBinding> </bindings> <client> <endpoint address="http://118.186.240.182:8090/MyServiceOne.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IServiceOne" contract="MyWCFOne.IServiceOne" name="WSHttpBinding_IServiceOne" behaviorConfiguration="myClientBehavior"> <identity> <dns value="MyServerCert" /> </identity> </endpoint> </client> <behaviors> <endpointBehaviors> <behavior name="myClientBehavior"> <clientCredentials> <serviceCertificate> <authentication certificateValidationMode="Custom" customCertificateValidatorType="WCFClient.MyX509Validator,WCFClient"/> </serviceCertificate> </clientCredentials> </behavior> </endpointBehaviors> </behaviors> </system.serviceModel> </configuration>OK,客户端代码和配置完成,现在你可以运行自己的程序了,运行界面如下:
点击提交,会调用远程,GetData然后显示在文本框内,如下图所示: