[WCF安全系列]谈谈WCF的客户端认证[用户名/密码认证]

对于基于Internet的应用,基于用户名和密码的认证方式是最为常用的,而WCF为你提供了不同模式的用户名认证方式。首先还是从用户凭证的表示说起。

一、用户名/密码认证的三种模式

基于用户名/密码的用户凭证通过类型UserNamePasswordClientCredential表示。而在ClientCredentials中,只读属性UserName表示这样一个用户凭证。你可以按照Windows凭证的方式为ChannelFactory<TChannel>或者ClientBase<TChannel>基于用户名/密码凭证。

   1: public class ClientCredentials
   2: {
   3:      //其他成员
   4:      public UserNamePasswordClientCredential UserName { get; }
   5: } 
   6: public sealed class UserNamePasswordClientCredential
   7: {
   8:     //其他成员
   9:     public string Password {get; set; }
  10:     public string UserName { get; set; }
  11: }

用户名/密码凭证在客户端的设置很容易,但是我们关心的是服务端采用怎样的机制来验证这个凭证。WCF为你提供了如下三种方式来验证凭证中用户名是否和密码相符:

  • Windows:将用户名和密码映射为Windows帐号和密码,采用Windows认证;
  • MembershipProvider:利用配置的MembershipProvider验证用户名和密码;
  • 自定义:通过继承抽象类UsernamePasswordValidator,自定义用户名/密码验证器进行验证。

WCF通过枚举UserNamePasswordValidationMode定了上述三种用户名/密码认证模式。该枚举定义如下,其中Windows是默认选项。

   1: public enum UserNamePasswordValidationMode
   2: {
   3:     Windows,
   4:     MembershipProvider,
   5:     Custom
   6: }

上述三种认证模式的设置最终通过之前提到过的ServiceCredentials这一服务行为进行设置的。从下面的定义我们可以看出,ServiceCredentials定义了只读属性UserNameAuthentication用于基于用户名/密码认证的相关设置。属性的类型为UserNamePasswordServiceCredential,定义其中的UserNamePasswordValidationMode属性表示采用的认证模式。如果选择了需要通过属性MembershipProvider设置采用的MembershipProvider。如果选择了Custom,则需要通过CustomUserNamePasswordValidator属性指定你自定义的UserNamePasswordValidator对象。

   1: public class ServiceCredentials: SecurityCredentialsManager, IServiceBehavior
   2: {
   3:     //其他成员
   4:      public UserNamePasswordServiceCredential UserNameAuthentication { get; }
   5: }
   6: public sealed class UserNamePasswordServiceCredential
   7: {
   8:     //其他成员
   9:     public UserNamePasswordValidator CustomUserNamePasswordValidator { get; set; }
  10:     public MembershipProvider MembershipProvider { get; set; }
  11:     public UserNamePasswordValidationMode UserNamePasswordValidationMode { get; set; }
  12: }

接下来我们通过实例演示的方式来如何通过MembershipProvider进行基于用户名/密码认证,而对于自定义UserNamePasswordValidator的实例我会在介绍安全会话的时候进行演示。

二、实例演示:通过MembershipProvider进行用户名/密码的认证

Membership是ASP.NET中一个重要的模块,旨在进行基于用户名/密码的认证和对应的帐号管理。Membership采用策略设计模式,所有的API通过几个静态Membership类暴露出来,而相应的功能实现在具体的Membership提供者中。所有的提供者继承自同一个抽象类MembershipProvider。ASP.NET提供了两种类型的提供者:SqlMembershipProviderActiveDirectoryMembershipProvider。前者将用户存储于SQL Server数据库中,而后者则直接建立在AD之上,本实例采用SqlMembershipProvider,在前面一个实例演示中,我们创建了以计算服务为场景的解决方案,现在我们直接沿用它。

我们首要的任务是在用于存储帐户信息的SQL Server数据库,为此你可以先在本地SQL
Server创建一个空的数据库(假设起名为AspNetDb)。你接着需要在该数据库中创建SqlMembershipProvider所需的数据表和相应的存储过程。这些数据库对象的创建,需要借助aspnet_regsql.exe这个工具。你只需要以命令行的方式执行如下aspnet_regsql.exe(无需任何参数),相应的向导就会出现。

在向导弹出的前两个窗体中保持默认设置,直接点击“下一步”后,会出现一个数据库选择窗体。此时你需要选择我们刚刚创建的数据库,点击“确认”后,相关的数据库对象会为你创建出来。

这些创建出来的数据表可以同时服务于多个应用,所有每一个表中都具有一个名称为ApplicationId的字段来明确该条记录对应的应用。而所有应用记录维护在aspnet_Applications这么一个表中。现在我们需要通过执行下面一段SQL脚本在该表中添加一条表示我们应用的记录。我们应用起名为MembershipAuthenticationDemo。

   1: INSERT INTO [aspnet_Applications]
   2:            ([ApplicationName]
   3:            ,[LoweredApplicationName]
   4:            ,[ApplicationId]
   5:            ,[Description])
   6: VALUES
   7:            (
   8:              'MembershipAuthenticationDemo'
   9:              ,'membershipauthenticationdemo'
  10:              ,NEWID()
  11:              ,''
  12:         )

现在数据库方面已经准备就绪,我们接着来完成编程和配置方面的工作。我们不打算从新创建一个解决方案,而是直接对之前演示的实例进行改造。我们采用自我寄宿的方式,由于Membership隶属于ASP.NET,所以我们需要添加System.Web.dll的引用,如果你采用的是.NET
Frameowrk 4.0(本例所示的配置也是基于该版本),你还需额外添加对System.Web.ApplicationServices.dll的引用。接下来,我们需要在服务寄宿方面所做的工作就是将下面一段配置整个拷贝到app.config中。

   1: <?xml version="1.0"?>
   2: <configuration>
   3:   <connectionStrings>
   4:     <add name="AspNetDb" connectionString="Server=.; Database=AspNetDb; Uid=sa; Pwd=password"/>
   5:   </connectionStrings>
   6:   <system.web>
   7:     <membership defaultProvider="myProvider">
   8:       <providers>
   9:         <add name="myProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" 
  10:              connectionStringName="AspNetDb" applicationName="MembershipAuthenticationDemo" requiresQuestionAndAnswer="false"/>
  11:       </providers>
  12:     </membership>
  13:   </system.web>
  14: <system.serviceModel>
  15:   <bindings>
  16:     <ws2007HttpBinding>
  17:       <binding name="userNameCredentialBinding">
  18:         <security mode="Message">
  19:           <message clientCredentialType="UserName"/>
  20:         </security>
  21:       </binding>
  22:     </ws2007HttpBinding>
  23:   </bindings>
  24:   <services>
  25:     <service name="Artech.WcfServices.Services.CalculatorService" behaviorConfiguration="membershipAuthentication">
  26:       <endpoint address="http://127.0.0.1/calculatorservice" binding="ws2007HttpBinding" bindingConfiguration="userNameCredentialBinding" contract="Artech.WcfServices.Contracts.ICalculator"/>
  27:     </service>
  28:   </services>
  29:   <behaviors>
  30:     <serviceBehaviors>
  31:       <behavior  name="membershipAuthentication">
  32:         <serviceCredentials>
  33:           <serviceCertificate storeLocation="LocalMachine" storeName ="My" x509FindType="FindBySubjectName" findValue="Jinnan-PC"/>
  34:           <userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="myProvider"/>
  35:         </serviceCredentials>
  36:       </behavior>
  37:     </serviceBehaviors>
  38:   </behaviors>
  39:   </system.serviceModel>
  40: </configuration>

考虑到有些人可能对ASP.NET下的Membership相关配置不太了解,在这里我对上述这段配置进行以下简单的说明。

  • 配置名称为AspNetDb的连接字符串连接的是我们刚刚创建的数据库,并通过aspnet_regsql.exe工具在该数据库中创建了所需的数据库对象;
  • 表示Membership配置节的<system.web>/<membership>节点下配置了唯一的SqlMembershipProvider,配置名称为myProvider。上面配置的连接字符创名称AspNetDb配置在connectionStringName属性中,意味着该SqlMembershipProvider会将我们创建的数据库作为用户帐号存储;
  • 服务终结点采用WS2007HttpBinding,采用Message安全模式,客户端凭证类型被设置为UserName;
  • 服务应用了一个配置名称为membershipAuthentication的服务行为,该行为中通过<serviceCertificate>节点设置了服务证书。在表示用户名/密码认证配置的<userNameAuthentication>节点中,将认证模式设置成MembershipProvider,而membershipProviderName属性的值为我们在<system.web>/<membership>中设置的MembershipProvider的名称。

到目前为止,在我们创建的数据库中并没有用户帐户记录。为了演示认证的效果,我们必须创建相关用户帐户记录。为了省事,我直接将相关的代码写在了服务寄宿的代码中。如下面的代码片断所示,在对服务进行寄宿之前,我通过调用Membership的静态方法CreateUser创建了一个用户名、密码和Email分别为Zhansan、Pass@word和zhanshan@gmail.com的帐号。

   1: if (Membership.FindUsersByName("Zhansan").Count == 0)
   2: {
   3:     Membership.CreateUser("Zhansan", "Pass@word", "zhanshan@gmail.com");
   4: }
   5: using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
   6: {
   7:     host.Open();
   8:     Console.Read();
   9: }

接下来我们需要对客户端的配置进行相应的调整,整个配置内容如下面的XML片断所示。对于这段配置有一点需要注意的是:终结点应用了一个名称为peerTrustSvcCertValidation的行为,该行为中将服务证书认证模式设置成PeerTrust,所以你需要通过MMC证书管理单元的导出/导入功能将Jinnan-PC证书导入到“受信任人(Trusted People)”存储区。

   1: <?xml version="1.0"?>
   2: <configuration>
   3:   <system.serviceModel>
   4:     <bindings>
   5:       <ws2007HttpBinding>
   6:         <binding name="userNameCredentialBinding">
   7:           <security mode="Message">
   8:             <message clientCredentialType="UserName"/>
   9:           </security>
  10:         </binding>
  11:       </ws2007HttpBinding>
  12:     </bindings>
  13:     <client>
  14:       <endpoint name="calculatorService" behaviorConfiguration="peerTrustSvcCertValidation"  address="http://127.0.0.1/calculatorservice" binding="ws2007HttpBinding" bindingConfiguration="userNameCredentialBinding" contract="Artech.WcfServices.Contracts.ICalculator">
  15:         <identity>
  16:           <certificateReference storeLocation="LocalMachine" storeName ="My" x509FindType="FindBySubjectName" findValue="Jinnan-PC"/>
  17:         </identity>
  18:       </endpoint>
  19:     </client>
  20:     <behaviors>
  21:       <endpointBehaviors>
  22:         <behavior name="peerTrustSvcCertValidation">
  23:           <clientCredentials>
  24:             <serviceCertificate>
  25:               <authentication certificateValidationMode="PeerTrust"/>
  26:             </serviceCertificate>
  27:           </clientCredentials>
  28:         </behavior>
  29:       </endpointBehaviors>
  30:     </behaviors>
  31:   </system.serviceModel>
  32: </configuration>

最后,我么来编写如下一段客户端进行服务调用的程序。在下面的代码中,我进行了两次服务调用。但是创建服务代理对象的ChannelFactory<ICalculator>被设置了不同的用户名凭证。其中第一个是正确的用户名和密码,后一个却指定了一个根本不存在的用户名。

   1: using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculatorService"))
   2: {
   3:     UserNamePasswordClientCredential credential = channelFactory.Credentials.UserName;
   4:     credential.UserName     = "Zhansan";
   5:     credential.Password     = "Pass@word";
   6:     ICalculator calculator  = channelFactory.CreateChannel();
   7:     calculator.Add(1, 2);
   8:     Console.WriteLine("服务调用成功...");
   9: }
  10: using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculatorService"))
  11: {
  12:     UserNamePasswordClientCredential credential = channelFactory.Credentials.UserName;
  13:     credential.UserName     = "lisi";
  14:     credential.Password     = "Pass@word";
  15:     ICalculator calculator  = channelFactory.CreateChannel();
  16:     try
  17:     {
  18:         calculator.Add(1, 2);
  19:     }
  20:     catch
  21:     {
  22:         Console.WriteLine("服务调用失败...");
  23:     }
  24: }

输出结果:

   1: 服务调用成功...
   2: 服务调用失败...

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2024-10-27 03:04:03

[WCF安全系列]谈谈WCF的客户端认证[用户名/密码认证]的相关文章

[WCF安全系列]谈谈WCF的客户端认证[X.509证书认证]

前面介绍Windows认证和用户名/密码认证这两种典型的客户端认证模式,我们最后来介绍最后一种客户端认证方式,即客户端凭证类型为X.509证书时服务端采用的认证,简称为证书认证.我们照例先看看看客户端证书凭证如何设置设置. 一.客户端证书凭证的设置 在服务认证一文中,我们知道了基于X.509证书证书的服务凭证通过X509CertificateRecipientServiceCredential类型表示.与之对应地,客户端凭证对应的类型是X509CertificateInitiatorClient

[WCF安全系列]谈谈WCF的客户端认证[Windows认证]

结束了服务认证的介绍之后,我们接着介绍WCF双向认证的另一个方面,即服务对客户端的认证,简称客户端认证.客户端认证采用的方式决定于客户端凭证的类型,内容只要涉及基于以下三种典型客户凭证类型的认证:Windows.用户名和X.509证书.从编程的角度来讲,Windows认证是最为简单的认证方式.在这种认证方式下,客户端进程运行的Window帐号对应的Windows凭证被自动作为调用服务的客户端凭证,所以无需显示指定具体的Windiws凭证. 如果需要另一个Windows帐号的名义调用服务,客户端就

[WCF安全系列]认证与凭证:用户名/密码认证与Windows认证

如果要给认证下一个定义,我个人的倾向这样的定义:认证是确定被认证方的真实身份和他或她申明(Claim)的身份是否相符的行为.认证方需要被认证方提供相应的身份证明材料,以鉴定本身的身份是否与声称的身份相符.在计算机的语言中,这里的身份证明有一个专有的名称,即"凭证(Credential)",或者用户凭证(User Credential).认证凭证(Authentication Credential). 一.凭证的属性 最好的设计就是能够尽可能的模拟现实的设计.对于安全认证来说,在现实生活

WCF的用户名密码认证

以前我们用WebService做分布式系统的时候,认证是个麻烦的问题,通常的做法是继承一个SoapHeader,把用户名和密码放到里面,每调用一个方法都要把用户名和密码传递给服务器端来验证 ,效率相当低,代码编写相当的麻烦,而且还不安全! WCF支持多种认证技术,例如Windowns认证.X509证书.Issued Tokens.用户名密码认证等,在跨Windows域分布的系统中,用户名密码认证还是比较常用的,要实现用户名密码认证,就必须需要X509证书,为什么呢?因为我们需要X509证书这种非

详解Nginx实战之让用户通过用户名密码认证访问web站点_nginx

有时我们会有这么一种需求,就是你的网站并不想提供一个公共的访问或者某些页面不希望公开,我们希望的是某些特定的客户端可以访问.那么我们可以在访问时要求进行身份认证,就如给你自己的家门加一把锁,以拒绝那些不速之客. 实验环境: 现在公司开通了一个内部使用网站为www1.rsyslog.org,要求所有人查看网页信息之前都必须要输入内定的用户名及密码方可浏览网页 使用nginx下虚拟主机的搭建 www1.rsyslog.org进行测试. www1.rsyslog.org 192.168.100.107

squid添加用户名密码认证

国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私募机构九鼎控股打造,九鼎投资是在全国股份转让系统挂牌的公众公司,股票代码为430719,为"中国PE第一股",市值超1000亿元.  -------------------------------------------------------- vim /etc/squid/squid.

[WCF安全系列]认证与凭证:X.509证书

在<上篇>中,我们谈到了常用的认证方式:用户名/密码认证和Windows认证.在下篇中,我们着重来介绍另外一种重要的凭证类型:X.509证书,以及针对X.509证书的认证方式.不过为了让读者能够真正地全面地了解X.509证书,我们需要先了解一些关于非对称密码学的背景知识. 目录 一.非对称密码学(Asymmetric Cryptography)     消息加密(Encryption)     数字签名(Digital Signature) 二.数字证书     数字证书的颁发机制     创

[WCF安全系列]绑定、安全模式与客户端凭证类型:BasicHttpBinding

整个安全传输是在WCF的信道层进行的,而绑定是信道层的缔造者,所以终结点采用哪种类型的绑定以及对绑定的属性进行怎样的设置决定了信道层最终采用何种机制实现消息的安全传输.具体来说,我们可以通过绑定设置最终采用的安全模式,以及基于相应安全模式下进行认证和消息保护的行为. 一.Binding安全相关的应用编程接口 不同的绑定类型由于其采用的传输协议不同,应用的场景也各有侧重,很难提供一种统一的应用编程接口完成基于不同绑定的安全设置,所以每一种绑定都具有各自用于安全设置相关的类型.但是基于对安全的设置,

[WCF安全系列]实例演示:TLS/SSL在WCF中的应用[SSL over TCP]

在接下来的系列文章中我们正是讨论关于身份认证的主题.在前面我们已经谈到了,WCF中的认证属于"双向认证",既包括服务对客户端的认证(以下简称客户端认证),也包括客户端对服务的认证(以下简称服务认证).客户端认证和服务认证从本质上并没有什么不同,无非都是被认证一方提供相应的用户凭证供对方对自己的身份进行验证.我们先来讨论服务认证,客户端认证放在后续的文章中. 在<从两种安全模式谈起>中,我们对TLS/SSL进行了简单的介绍.我们知道,客户端和服务在为建立安全上下文而进行的协商