如何实现基于消息安全验证机制下的username身份验证过程
前言:
WCF提供了多种模式的传送安全机制(transfer security mode),分别为None(没有任何安全机制),Transport security,Message security和TransportWithMessageCredential. Transport security和Message security有很多差别, 例如:Message security可是在消息的初始发送者和最后的接受者之间的任意数目的节点上保持,然而传输安全只能在两个节点之间保证传输的安全性。另外,messeage security 相对于transport security mode,提供了更多的验证传输源的机制。比如transport security提供的验证客户端身份类型包括:None,Basic,Digest,Windows和certificate,而message security提供的验证客户端身份验证类型包括:None,Windows,UserName,Certificate,IssuedToken. 本文将介绍如何通过message security mode 的username身份验证机制,配合服务器端certificate的使用,验证客户身份和保证传输数据的安全性。
(For more information about the difference between transport security mode&message security mode, please refer to:http://msdn.microsoft.com/en-us/library/ms789011.aspx)
正文:
首先介绍基于message security模式下的username身份验证机制,如下图所示:
过程简单描述如下:
step1:
首先在WCF Client与WCF service建立一个安全连接之后,会协商得到一个双方都支持的会话加密算法(对称加密算法,比如DES)和会话密钥(该过程其实相当复杂,有关如何利用certificate建立安全会话,请参阅其他文档),然后将客户端提供的username和password通过加密密钥加密后传送至服务器端。
step2:
WCF service将提供一个基于username的验证器。往往我们会需要重写System.IdentityModel.Selectors.UserNamePasswordValidator的Validate()方法,来自定义身份验证过程。比如我们在数据库中验证用户合法性,或者验证用户能否映射到一个windows域帐号(获取当前安全上下文的用户身份方法为:
WindowsIdentity identity=ServiceSecurityContext.Current.WindowsIdentity)。
step3:
如果身份验证通过,则可以继续客户端request的service,并返回Response;否则抛出System.IdentityModel.Tokens.SecurityTokenException的异常,并且连接终止。
实例演示:
1. 首先我们定义一个简单的WCF service.代码如下:
2. 然后添加app.config,并进行相应配置。
关键是配置邦定协议时,要指定security mode为message,同时将MessageClientCredentialsType设置为UserName。如下图所示:
同时设置创建一个新的serviceBehavior configuration,并将UserNameAuthentication element中的UserNamePasswordAuthenticationMode设置为Custom,如下图所示:
然后我们自定义一个继承自抽象类System.IdentityModel.Selectors.UserNamePasswordValidator的类,并override基类的抽象方法:Validate(string username,string password).代码如下:
注意:在前面设置CustomUserNamePasswordValidatorType时,已经将其设置为我们自定义的身份验证类MyUserNamePasswordValidator.
所以配置完成后,点击run, 看看出现什么?
程序会抛出如下exception:
god! what happened? 原来当我们采用UserNamePassword进行client Authencation的时候,UserName和Password决不能够采用明文在网络上传输,因此服务器端必须提供一个certificate来对会话进行加密。否则会无法建立一个安全的信道监听器,从而导致抛出以上exception.
因此解决方法就是要在服务器端安装一个serviceCertificate了。 然后点击运行,期待的WCF Service终于online了。哈哈。
好了 下面开始创建WCF Client
利用svcutil得到Service的proxy和app.config后,将其加入到客户端project. 客户端代码如下:
点击运行。
god, 程序抛出exception 如下:
what happend?
原来服务器端提供的certificate无法在client获得验证通过,也就是说客户端按照他的certificateAuthenticationMode(by default :chain trust)的话,其根本不认识服务器所提供的certificate。因此throw所谓的安全沟通(security negotiation)的exception信息。解决方法就是把客户端的certificateAuthencationMode作适当改变即可,设置成为当前模式下可以通过的验证模式,比如none(根本不验证其有效性),custom(自定义其有效性,此时我们需要自定义一个继承自System.IdentityModel.Selectors.X509CertificateValidator的类,并且override 抽象方法Validate(X509Certificate2 certificate)即可。为简便起见,本例设置为none
实际上,在基于username/password的client验证模式中,客户端不太有必要提供对加密公钥的验证了。但我们用svcutil从WCF service获得其client端config文件时,会将加密Public key包含在config文件中一起生成客户端配置文件,这样客户端就无须要再安装服务器端提供的加密公钥了,其产生的客户端公钥如下所示:
如下图所示:
ok, 点击运行,成功!
运行screenshot如下:
客户端:
服务器端: