概述

今天在做Master Data Service(后面简称MDS)项目时需要通过WCF来使用MDS的API,从而对MDS的数据进行操作。在这个过程中,遇到了一个棘手的问题,就是在客户端调用Web Service时的身份认证问题,于是乎对WCF的认证方式做了一个简单的了解。在这里还要感谢蔡总陪我加班一起解决问题,在蔡总的大力支持下问题得以解决。我们解决问题的方式采用了客户端用户名+密码的方式来进行身份认证,这只是诸多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进行用户名+密码的认证

Membership是ASP.NET中一个重要的模块,旨在进行基于用户名/密码的认证和对应的帐号管理。Membership采用策略设计模式,所有的API通过几个静态Membership类暴露出来,而相应的功能实现在具体的Membership提供者中。所有的提供者继承自同一个抽象类MembershipProvider。ASP.NET提供了两种类型的提供者:SqlMembershipProvider和ActiveDirectoryMembershipProvider。前者将用户存储于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,
  10. PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="AspNetDb" applicationName="MembershipAuthenticationDemo"
  11. requiresQuestionAndAnswer="false"/>
  12. </providers>
  13. </membership>
  14. </system.web>
  15. <system.serviceModel>
  16. <bindings>
  17. <ws2007HttpBinding>
  18. <binding name="userNameCredentialBinding">
  19. <security mode="Message">
  20. <message clientCredentialType="UserName"/>
  21. </security>
  22. </binding>
  23. </ws2007HttpBinding>
  24. </bindings>
  25. <services>
  26. <service name="Artech.WcfServices.Services.CalculatorService" behaviorConfiguration="membershipAuthentication">
  27. <endpoint address="http://127.0.0.1/calculatorservice" binding="ws2007HttpBinding"
  28. bindingConfiguration="userNameCredentialBinding" contract="Artech.WcfServices.Contracts.ICalculator"/>
  29. </service>
  30. </services>
  31. <behaviors>
  32. <serviceBehaviors>
  33. <behavior  name="membershipAuthentication">
  34. <serviceCredentials>
  35. <serviceCertificate storeLocation="LocalMachine" storeName ="My" x509FindType="FindBySubjectName" findValue="YueXu-PC"/>
  36. <userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="myProvider"/>
  37. </serviceCredentials>
  38. </behavior>
  39. </serviceBehaviors>
  40. </behaviors>
  41. </system.serviceModel>
  42. </configuration>

至此,在我们创建的数据库中并没有用户帐户记录。为了演示认证效果,我们需要创建相关用户帐户记录。为了方便,我直接将相关的代码写在了服务寄宿的代码中。如下面的代码片断所示,在对服务进行寄宿之前,我通过调用Membership的静态方法CreateUser创建了一个用户名、密码和Email分别为xuyue、password01和xuyue1000@hotmail的帐号。

  1. if (Membership.FindUsersByName("xuyue").Count == 0)
  2. {
  3. Membership.CreateUser("xuyue", "password01", "xuyue1000@hotmail.com");
  4. }
  5. using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
  6. {
  7. host.Open();
  8. Console.Read();
  9. }

接下来我们需要对客户端的配置进行相应的调整,整个配置内容如下面的XML片断所示。对于这段配置有一点需要注意的是:终结点应用了一个名称为peerTrustSvcCertValidation的行为,该行为中将服务证书认证模式设置成PeerTrust,所以你需要通过MMC证书管理单元的导出/导入功能将YueXu-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"
  15. address=<a href="http://127.0.0.1/calculatorservice">http://127.0.0.1/calculatorservice</a> binding="ws2007HttpBinding"
  16. bindingConfiguration="userNameCredentialBinding" contract="Artech.WcfServices.Contracts.ICalculator">
  17. <identity>
  18. <certificateReference storeLocation="LocalMachine" storeName ="My" x509FindType="FindBySubjectName" findValue="YueXu-PC"/>
  19. </identity>
  20. </endpoint>
  21. </client>
  22. <behaviors>
  23. <endpointBehaviors>
  24. <behavior name="peerTrustSvcCertValidation">
  25. <clientCredentials>
  26. <serviceCertificate>
  27. <authentication certificateValidationMode="PeerTrust"/>
  28. </serviceCertificate>
  29. </clientCredentials>
  30. </behavior>
  31. </endpointBehaviors>
  32. </behaviors>
  33. </system.serviceModel>
  34. </configuration>

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

  1. using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculatorService"))
  2. {
  3. UserNamePasswordClientCredential credential = channelFactory.Credentials.UserName;
  4. credential.UserName     = "xuyue";
  5. credential.Password     = "password01";
  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     = "wrongName";
  14. credential.Password     = "wrongPWD";
  15. ICalculator calculator  = channelFactory.CreateChannel();
  16. try
  17. {
  18. calculator.Add(1, 2);
  19. }
  20. catch
  21. {
  22. Console.WriteLine("服务调用失败...");
  23. }
  24. }

输出结果:

1: 服务调用成功...

2: 服务调用失败...

WCF的用户名+密码认证方式(转)的更多相关文章

  1. WCF身份验证之用户名密码认证

    WCF支持多种认证技术,例如Windowns认证.X509证书.Issued Tokens.用户名密码认证等,在跨Windows域分布的系统中,用户名密码认证是比较常用的,要实现用户名密码认证,就必须 ...

  2. Geoserver通过ajax跨域访问服务数据的方法(含用户名密码认证的配置方式)

    Goeserver数据有两种,一种需进行用户密码的权限认证,一种无须用户密码.对于网上跨域访问Geoserver数据的种种方法,对这2种数据并非通用. 笔者将Geoserver官方下载的Geoserv ...

  3. WCF用户名密码验证方式

    WCF使用用户名密码验证 服务契约 namespace WCFUserNameConstract { [ServiceContract] public interface IWcfContract { ...

  4. MySQL 8.0.14 新的密码认证方式和客户端链接

    MySQL 8.0.14 新的密码认证方式和客户端链接 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   MySQL8.0在密码认证方式发生了改变,这也是有点小伙伴在MySQL创建 ...

  5. Nginx实战之让用户通过用户名密码认证访问web站点

    1.Nginx实战之让用户通过用户名密码认证访问web站点 [root@master ~]# vim /usr/local/nginx/conf/extra/www.conf server { lis ...

  6. nginx让用户通过用户名密码认证访问web页面

    在使用nginx转发的时候,要进行一次用户身份的确认. 1)通过htpasswd命令生成用户名及对应密码数据库文件. [root@bgs-5p173-wangwenting ~]# htpasswd ...

  7. 启动Nginx目录浏览功能及 让用户通过用户名密码认证访问web站点

    一.启动Nginx目录浏览功能  [root@abcdocker extra]# cat w.conf server { listen 80; server_name IP地址; location / ...

  8. WCF之添加自定义用户名密码认证

    1.创建WCF服务应用以及调用客户端(请自行google).  2.创建X509证书       cmd 进入  C:\Program Files\Microsoft SDKs\Windows\v6. ...

  9. OpenVPN使用用户名/密码验证方式

    OpenVPN推荐使用证书进行认证,安全性很高,但是配置起来很麻烦.还好它也能像pptp等vpn一样使用用户名/密码进行认证. 不管何种认证方式,服务端的ca.crt, server.crt, ser ...

随机推荐

  1. 二、Silverlight中使用MVVM(二)——提高

    在第一篇文章中的示例中,我们已经简单的了解了应用MVVM模式的流程,我的本意是你已经了解了一点MVVM的概念,然后又没有一个较好的例子学习,可以跟着我一起学习MVVM模式,所以这个部分,都是没有理论知 ...

  2. 对于一个有序数组,我们通常采用二分查找的方式来定位某一元素,请编写二分查找的算法,在数组中查找指定元素。 给定一个整数数组A及它的大小n,同时给定要查找的元素val,请返回它在数组中的位置(从0开始),若不存在该元素,返回-1。若该元素出现多次,请返回第一次出现的位置。

    // ConsoleApplication10.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream& ...

  3. iOS_39_触摸解锁

    终于效果图: 控制器: // // BeyondViewController.m // 39_触摸解锁 // // Created by beyond on 14-9-17. // Copyright ...

  4. 基于红帽5裁剪一个简单的Linux

    HOST:宿主机 Target:目标机 1.基于HOST制作一个简单的可启动的Linux 1.给目标磁盘分区 两个: 在宿主机上:/dev/sdb1,/dev/sdb2 /dev/sdb1挂载到 /m ...

  5. 获得手机的ip

    本文转载至 http://blog.csdn.net/showhilllee/article/details/8746114      iosip手机 貌似ASI里获取ip地址的链接不可以了.也曾试过 ...

  6. Centos7升级python版本

    升级Python 我的安装目录是/home/python 下载 ` cd /home/python wget https://www.python.org/ftp/python/3.5.0/Pytho ...

  7. java中随机生成汉字

    main方法中使用: //随机生成100个汉字 String ss=""; for(int i=0;i<100;i++){ ss+=getChinese(i); } Syst ...

  8. 开发笔记--java.lang.OutOfMemoryError: PermGen space异常处理

    第一次到公司开发项目,比起之前的小项目来说这次的项目特别大,以至于运行之后出现了java.lang.OutOfMemoryError: PermGen space的异常,从字面意思上来看是内存溢出的原 ...

  9. perl之创建临时文件夹遇到同名文件该咋办

    当你在目录下进行一系列操作时,若要创建许多文件或者修改文件,可能会遇到许多麻烦的事.所以呢,新建一个文件夹,然后在这个文件夹下新建文件或者修改文件.假设,你的代码要在一个目录下新建一个文件夹,名为Tm ...

  10. Java多线程系列 基础篇05 synchronized关键字

    1. synchronized原理 在java中,每一个对象有且仅有一个同步锁,所以同步锁是依赖于对象而存在.当我们调用某对象的synchronized方法时,就获取了该对象的同步锁.例如,synch ...