本篇不是介绍WCF的基础知识,而专门讨论基于UserName的身份验证方式。

关于WCF的基础知识,如果有不清楚的,请参考下面的链接

http://msdn.microsoft.com/zh-cn/library/ms735119.aspx

 

我现在用一个实录的方式,介绍一下UserName的身份验证

1. 准备一个证书。这个证书将用来做消息的加密。

我们可以通过SDK工具makecert创建一个测试证书,如下图

makecert -r -pe -n "CN=WCFCert" -ss My -sky exchange

WCF 之UserName身份验证全记录

创建成功之后,我们可以在证书管理器中看到我们的证书

WCF 之UserName身份验证全记录

WCF 之UserName身份验证全记录

 

2. 创建WCF服务库

WCF 之UserName身份验证全记录

修改名称,点击“确定”后创建的项目已经包含了一个简单的接口和服务

WCF 之UserName身份验证全记录 WCF 之UserName身份验证全记录 WCF 之UserName身份验证全记录

3. 测试该服务

通过VS2008提供的工具,我们可以很方便地进行服务的测试工作。方法是:直接按F5

WCF 之UserName身份验证全记录

双击“GetData”

WCF 之UserName身份验证全记录

在“值”中输入一个数值,点击“调用”

WCF 之UserName身份验证全记录

点击“确定”后,在测试客户端的右下部分,会看到有关的响应

WCF 之UserName身份验证全记录

测试结果表示该服务是工作正常的。

 

4.创建一个客户端来使用该服务

我们通过创建一个Windows Forms程序来作为客户端。

WCF 之UserName身份验证全记录

项目创建好之后,我们需要为该项目添加一个服务引用

WCF 之UserName身份验证全记录

点击“添加服务引用”

WCF 之UserName身份验证全记录

点击“发现”按钮

WCF 之UserName身份验证全记录

点击“确定”按钮

WCF 之UserName身份验证全记录

5. 编写代码访问该服务

private void btGetData_Click(object sender, EventArgs e)
{
    TestService.Service1Client proxy = new Client.TestService.Service1Client();
    MessageBox.Show(proxy.GetData(2000));
}

 

6. 将客户端程序设置为启动项目,按F5调试

WCF 之UserName身份验证全记录

 

7. 现在的服务能正常工作了,但没有做任何的身份验证。这不能满足我们的需求。我们首先从服务端下手,进行设置

WCF 之UserName身份验证全记录

选择中WCFServiceLib中的app.config文件,然后右键菜单中点击“编辑WCF配置”

WCF 之UserName身份验证全记录

我们可以看到Service1这个服务,会有两个EndPoint(终结点)。其中第一个是标准的请求的终结点,而第二个(mex)是基于元数据的发现。

我们点击第一个终结点(wsHttpBinding)下面的Binding configuration右边的 “单击可创建”

WCF 之UserName身份验证全记录

如果有兴趣,可以逐一了解每个属性。我们这里就不一一介绍了。因为此篇blog的目的是解决安全性问题

点击窗口顶部的“安全性”

WCF 之UserName身份验证全记录

我们修改两个选项如下。即:在消息级别使用基于UserName的身份验证,而在传输级别不使用验证

WCF 之UserName身份验证全记录

接下来,我们还要配置服务行为。通过依次展开“高级”=》“服务行为”=》WCFServiceLib.Service1Behavier。我们能看到如下的界面

WCF 之UserName身份验证全记录

点击“添加”按钮

WCF 之UserName身份验证全记录

选择“serviceCredentials”,点击“添加”

WCF 之UserName身份验证全记录

双击“serviceCredentials”

WCF 之UserName身份验证全记录

这个窗口中主要要设置的是UserNamePasswordValidationMode。这里有三个选择,如下图所示

WCF 之UserName身份验证全记录

  • Windows :基于windows的用户名和密码
  • MembershipProvider :基于成员管理提供程序
  • Custom : 自定义的身份验证提供程序

我们先用Windows验证方式。

WCF 之UserName身份验证全记录 

我们还需要设置服务端证书的信息,如下

WCF 之UserName身份验证全记录

设置完之后,我们选择“文件”=》“保存”后,回到Visual Studio中的服务项目中的app.config

<system.serviceModel>
   <bindings>
     <wsHttpBinding>
       <binding name="NewBinding0">
         <security>
           <transport clientCredentialType="None" />
           <message clientCredentialType="UserName" />
         </security>
       </binding>
     </wsHttpBinding>

   </bindings>
   <services>
     <service behaviorConfiguration="WCFServiceLib.Service1Behavior"
       name="WCFServiceLib.Service1">
       <endpoint address="" binding="wsHttpBinding" bindingConfiguration="NewBinding0"
         contract="WCFServiceLib.IService1">
         <identity>
           <dns value="localhost" />
         </identity>
       </endpoint>
       <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
       <host>
         <baseAddresses>
           <add baseAddress="http://localhost:8731/Design_Time_Addresses/WCFServiceLib/Service1/" />
         </baseAddresses>
       </host>
     </service>
   </services>
   <behaviors>
     <serviceBehaviors>
       <behavior name="WCFServiceLib.Service1Behavior">
         <serviceMetadata httpGetEnabled="true" />
         <serviceDebug includeExceptionDetailInFaults="false" />
         <serviceCredentials>
           <serviceCertificate findValue="WCFCert" storeLocation="CurrentUser"
             x509FindType="FindBySubjectName" />
           <userNameAuthentication cacheLogonTokens="false" />

         </serviceCredentials>
       </behavior>
     </serviceBehaviors>
   </behaviors>
</system.serviceModel>

8. 完成服务端的配置后,我们先直接运行客户端程序。看看会发生什么情况

WCF 之UserName身份验证全记录

我们收到了一个异常消息。很显然,因为服务端已经启用了更加严格的安全性,客户端也需要有相应的设置才行

9. 对客户端进行配置

WCF 之UserName身份验证全记录

我们发现这个终结点有一个相应的配置。接下来对其进行查看,确定MessageClientCredentialType也是Windows

WCF 之UserName身份验证全记录

接下来,我们还需要定义终结点行为

WCF 之UserName身份验证全记录

点击“新建终结点行为配置”

WCF 之UserName身份验证全记录

点击“添加”

WCF 之UserName身份验证全记录

选择“clientCredentials”.依次展开clientCredentials=>serviceCertificate=>defaultcertificate,并进行如下的设置。这里的证书需要与服务器端匹配,因为它将对消息进行加密。

WCF 之UserName身份验证全记录

选择“身份验证”

WCF 之UserName身份验证全记录

【注意】一定要把CertificateValidationMode设置为None。因为我们现在使用的是测试证书

然后,回到终结点中,将终结点与行为进行关联。

WCF 之UserName身份验证全记录

并且,在“标识”页面进行如下的设置

WCF 之UserName身份验证全记录

完成如上的配置后,保存,退出该工具。

<system.serviceModel>
    <behaviors>
        <endpointBehaviors>
            <behavior name="NewBehavior">
                <clientCredentials>
                    <serviceCertificate>
                        <defaultCertificate findValue="WCFCert" x509FindType="FindBySubjectName" />
                        <authentication certificateValidationMode="None" />
                    </serviceCertificate>
                </clientCredentials>

            </behavior>
        </endpointBehaviors>
    </behaviors>
    <bindings>
        <wsHttpBinding>
            <binding name="WSHttpBinding_IService1" 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" establishSecurityContext="true" />
                </security>
            </binding>
        </wsHttpBinding>
    </bindings>
    <client>
        <endpoint address="http://localhost:8731/Design_Time_Addresses/WCFServiceLib/Service1/"
            behaviorConfiguration="NewBehavior" binding="wsHttpBinding"
            bindingConfiguration="WSHttpBinding_IService1" contract="TestService.IService1"
            name="WSHttpBinding_IService1">
            <identity>
                <certificateReference storeLocation="CurrentUser" x509FindType="FindBySubjectName"
                    findValue="WCFCert" />
            </identity>
        </endpoint>

    </client>
</system.serviceModel>

9. 修改客户端代码,在调用服务之前设置客户端的凭据

WCF 之UserName身份验证全记录

我们很高兴地看到,这样做了之后,就可以看到结果了

WCF 之UserName身份验证全记录

 

10. 进一步的扩展。

上面的过程我们实现了对于WCF服务,使用基于windows的username验证的方式。但如果我们想更加有灵活性.首先看我们能不能使用自定义的代码来进行验证呢?

 

11. 实现自定义的身份验证器

WCF 之UserName身份验证全记录

我们在服务项目中添加一个新的类

同时,我们还需要添加两个引用

WCF 之UserName身份验证全记录

然后,编写该类型

WCF 之UserName身份验证全记录

 

12. 在服务器端指定使用该验证器

WCF 之UserName身份验证全记录

13. 客户端掉用调试

【注意】客户端无需任何更改。我们还是像下面这样提供用户名和密码

WCF 之UserName身份验证全记录

因为这个用户名和密码是正确的,所以能正确返回。

反之,如果我们修改一下密码,例如改成password1,则会发生一个异常

WCF 之UserName身份验证全记录

这样就实现了自定义的身份验证工作。

 

14. 最后,其实我们还可以做得更加简单一些。就是不编写专门的用户密码验证器,而是直接使用ASP.NET 2.0新增加的所谓MemberShip的功能。(关于该功能,以及其配套的数据库,你可能需要参考其他的资料,这里不做介绍)

我们首先在服务端配置文件中,添加如下的设置

  <connectionStrings>
  <add name="SqlServices" connectionString="server=(local);database=aspnetdb;integrated security=true"/>
</connectionStrings>
<system.web>
  <compilation debug="true" />
    <membership defaultProvider="SqlProvider">
    <providers>
      <clear />
      <add name="SqlProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="SqlServices" applicationName="demosite" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="true" requiresUniqueEmail="true" passwordFormat="Hashed" />
    </providers>
  </membership>
</system.web>

然后,继续配置服务的配置文件

WCF 之UserName身份验证全记录

同样,保存好配置后,客户端亦无须任何修改。直接调试一下看看

WCF 之UserName身份验证全记录

我们在数据库中有一个用户叫wcf,它的密码就是pass@word

所以,通过如下的客户端代码就可以成功实现验证

WCF 之UserName身份验证全记录

相关文章: