VS2010 WCF用户名密码X.509验证

来源:互联网 发布:中文域名成功案例 编辑:程序博客网 时间:2024/06/05 09:24

        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然后显示在文本框内,如下图所示:



原创粉丝点击