WCF支持多种认证技术,例如Windowns认证、X509证书、Issued Tokens、用户名密码认证等,在跨Windows域分布的系统中,用户名密码认证还是比较常用的,要实现用户名密码认证,就必须需要X509证书,为什么呢?因为我们需要X509证书这种非对称密钥技术来实现WCF在Message传递过程中的加密和解密,要不然用户名和密码就得在网络上明文传递!详细说明就是客户端把用户名和密码用公钥加密后传递给服务器端,服务器端再用自己的私钥来解密,然后传递给相应的验证程序来实现身份验证。
服务器环境: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然后显示在文本框内,如下图所示: