WCF 之UserName身份验证全记录

本篇不是介绍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

image

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

image

image

2. 创建WCF服务库

image

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

image image image

3. 测试该服务

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

image

双击“GetData”

image

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

image

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

image

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

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

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

image

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

image

点击“添加服务引用”

image

点击“发现”按钮

image

点击“确定”按钮

image

5. 编写代码访问该服务

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

 

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

image

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

image

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

image

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

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

image

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

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

image

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

image

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

image

点击“添加”按钮

image

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

image

双击“serviceCredentials”

image

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

image

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

我们先用Windows验证方式。

image 

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

image

设置完之后,我们选择“文件”=》“保存”后,回到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. 完成服务端的配置后,我们先直接运行客户端程序。看看会发生什么情况

image

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

9. 对客户端进行配置

image

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

image

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

image

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

image

点击“添加”

image

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

image

选择“身份验证”

image

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

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

image

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

image

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

<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. 修改客户端代码,在调用服务之前设置客户端的凭据

image

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

image

10. 进一步的扩展。

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

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

image

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

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

image

然后,编写该类型

image

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

image

13. 客户端掉用调试

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

image

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

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

image

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

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>

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

image

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

image

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

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

image

原文地址:https://www.cnblogs.com/chenxizhang/p/1455582.html