在實際應(yīng)用中實現(xiàn)WCF用戶密碼認證
WCF框架是一款功能強大的分布式開發(fā)框架。對于初學(xué)者來說,可能其中有些功能不太熟悉。這需要我們在不斷的實踐中去慢慢研究這些功能。比如WCF用戶密碼認證的正確使用。#t#
以前我們用WebService做分布式系統(tǒng)的時候,認證是個麻煩的問題,通常的做法是繼承一個SoapHeader,把用戶名和密碼放到里面,每調(diào)用一個方法都要把用戶名和密碼傳遞給服務(wù)器端來驗證 ,效率相當(dāng)?shù)停a編寫相當(dāng)?shù)穆闊?,而且還不安全!
WCF支持多種認證技術(shù),例如Windowns認證、X509證書、Issued Tokens、用戶名密碼認證等,在跨Windows域分布的系統(tǒng)中,用戶名密碼認證還是比較常用的,要實現(xiàn)用戶名密碼認證,就必須需要X509證書,為什么呢?因為我們需要X509證書這種非對稱密鑰技術(shù)來實現(xiàn)WCF在Message傳遞過程中的加密和解密,要不然用戶名和密碼就得在網(wǎng)絡(luò)上明文傳遞!詳細說明就是客戶端把用戶名和密碼用公鑰加密后傳遞給服務(wù)器端,服務(wù)器端再用自己的私鑰來解密,然后傳遞給相應(yīng)的驗證程序來實現(xiàn)身份驗證。
當(dāng)然,做個測試程序就沒有必要去申請一個X509數(shù)字簽名證書了,微軟提供了一個makecert.exe的命令專門用來生成測試使用的X509證書的,那我們就來建立一個測試用的證書,在cmd下輸入以下命令:
makecert.exe -sr LocalMachine -ss My -a sha1 -n CN=MyServerCert -sky exchange –pe
這個命令的意思就是創(chuàng)建一個測試的X509證書,這個證書放在存儲位置為'Localmachine'的'My'這個文件夾下,證書主題名字叫'MyServerCert',需要更多關(guān)于makecert命令的信息請參考MSDN。
證書建立好了,我們就可以編寫代碼了,在VS2008下建立一個解決方案并在上面建立兩個Web項目,一個是'Asp.net Web 應(yīng)用程序'(客戶端),一個是'WCF服務(wù)應(yīng)用程序'(服務(wù)器端),我們先來編寫服務(wù)器端代碼,首先我們要編寫自己的WCF用戶密碼認證邏輯,先要在WCF項目上添加引用'System.IdentityModel'然后我們建立一個新的類文件并繼承自'System.IdentityModel.Selectors.UserNamePasswordValidator',然后我們重寫里面的'Validate'方法來實現(xiàn)用戶名密碼認證邏輯。代碼如下;
- using System;
 - using System.Collections.Generic;
 - using System.Linq;
 - using System.Text;
 - using System.IdentityModel.Selectors;
 - using System.IdentityModel.Tokens;
 - namespace ServerWcfService.CustomValidators
 - {
 - public class MyCustomValidator :
 
UserNamePasswordValidator- {
 - /// < summary>
 - /// Validates the user name and
 
password combination.- /// < /summary>
 - /// < param name="userName">
 
The user name.< /param>- /// < param name="password">
 
The password.< /param>- public override void Validate
 
(string userName, string password)- {
 - // validate arguments
 - if (string.IsNullOrEmpty(userName))
 - throw new ArgumentNullException("userName");
 - if (string.IsNullOrEmpty(password))
 - throw new ArgumentNullException("password");
 - // check if the user is not xiaozhuang
 - if (userName != "xiaozhuang" || password != "123456")
 - throw new SecurityTokenException("用戶名或者密碼錯誤!");
 - }
 - }
 - }
 
上面只是一個簡單的WCF用戶密碼認證,實際應(yīng)用中用戶名和密碼一般都保存在數(shù)據(jù)庫中,如果驗證不通過就拋出一個'SecurityTokenException'類型的異常;下一步我們需要配置一下服務(wù)端的webConfig文件,我的WebConfig文件Servicemodel配置節(jié)如下:
- < system.serviceModel>
 - < bindings>
 - < wsHttpBinding>
 - < binding name="mySecureBinding">
 - < security mode="Message">
 - < message clientCredentialType="UserName"/>
 - < /security>
 - < /binding>
 - < /wsHttpBinding>
 - < /bindings>
 - < services>
 - < service behaviorConfiguration=
 
"ServerWcfService.Services.MySimple
ServiceBehavior" name="ServerWcfService.
Services.MySimpleService">- < endpoint address="" binding=
 
"wsHttpBinding" contract="ServerWcfService.
ServiceContracts.IMySimpleService"
bindingConfiguration="mySecureBinding">- < identity>
 - < dns value="MyServerCert"/>
 - < /identity>
 - < /endpoint>
 - < endpoint address="mex" binding=
 
"mexHttpBinding" contract="IMetadataExchange"/>- < /service>
 - < /services>
 - < behaviors>
 - < serviceBehaviors>
 - < behavior name="ServerWcfService.
 
Services.MySimpleServiceBehavior">- < serviceMetadata httpGetEnabled="true"/>
 - < serviceDebug includeExceptionDetailInFaults="false"/>
 - < serviceCredentials>
 - < serviceCertificate findValue=
 
"MyServerCert" x509FindType="FindBySubjectName"
storeLocation="LocalMachine" storeName="My"/>- < userNameAuthentication userNamePassword
 
ValidationMode="Custom" customUserName
PasswordValidatorType="ServerWcfService.
CustomValidators.MyCustomValidator,ServerWcfService"/>- < /serviceCredentials>
 - < /behavior>
 - < /serviceBehaviors>
 - < /behaviors>
 - < /system.serviceModel>
 
加粗的那些是我加上去的或者在默認上修改了的。Bindings節(jié)指定了客戶端提供的認證類型為'username'并在endpoint節(jié)中指定bianding配置。在dns節(jié)中修改原來的localmachine為MyServerCert,當(dāng)然你也可以修改為別的,這取決于你的證書主題名稱是什么。也就是上面命令中的CN=MyServerCert,接下來我們加入在serviceCredentials配置節(jié),并在里面配置兩個小節(jié),ServiceCertificate節(jié)中指定了我們的X509證書的位置,以用來加解密message,usernameAuthentication節(jié)中指定了我們自己的WCF用戶密碼認證邏輯。
Sorry,忘了一件事情,就是寫一個測試的服務(wù)契約并實現(xiàn),寫法上和無認證的寫法一樣,如下
- ServerWcfService.Service
 
Contracts.IMySimpleService:- [OperationContract]
 - string PrintMessage
 
(string message);
這樣,服務(wù)端的代碼編寫和配置就完成了,生成項目測試一下,頁面顯示服務(wù)已生成成功。
接下來我們開始編寫客戶端代碼,先在客戶端引用剛才生成的WCF服務(wù),然后編寫客戶端代碼如下:
- protected void btnPrint_Click(object
 
sender, EventArgs e)- {
 - TestWCFService.MySimpleServiceClient
 
client = new ClientWeb.TestWCFService.
MySimpleServiceClient();- client.ClientCredentials.UserName.
 
UserName = "xiaozhuang";- client.ClientCredentials.UserName.
 
Password = "123456";- lbMessage.Text = client.PrintMessage
 
(txtMessage.Text);- }
 
如果你有一個真正的X509證書,那么現(xiàn)在的WCF用戶密碼認證代碼就可以正常運行了。但是很不幸,我們的證書是測試用的,我們運行的時候出錯:'X.509 certificate CN=MyServerCert 鏈生成失敗。所使用的證書具有無法驗證的信任鏈。請?zhí)鎿Q該證書或更改 certificateValidationMode。已處理證書鏈,但是在不受信任提供程序信任的根證書中終止',WCF無法驗證測試證書的信任鏈,那我們要做的就是繞過這個信任驗證,具體做法如下:
先要在Asp.net Web應(yīng)用程序項目上添加引用'System.IdentityModel'然后我們建立一個新的類文件并繼承自'System.IdentityModel.Selectors.X509CertificateValidator',然后我們重寫里面的'Validate'方法來實現(xiàn)我們自己的X509認證邏輯,代碼如下:
- using System;
 - using System.Configuration;
 - using System.IdentityModel.Selectors;
 - using System.IdentityModel.Tokens;
 - using System.Security.Cryptography.
 
X509Certificates;- namespace ClientWeb.CustomX509Validator
 - {
 - /// < summary>
 - /// Implements the validator for X509
 
certificates.- /// < /summary>
 - public class MyX509Validator:
 
X509CertificateValidator- {
 - /// < summary>
 - /// Validates a certificate.
 - /// < /summary>
 - /// < param name="certificate">
 
The certificate the validate.< /param>- public override void Validate
 
(X509Certificate2 certificate)- {
 - // validate argument
 - if (certificate == null)
 - throw new ArgumentNullException
 
("X509認證證書為空!");- // check if the name of the certifcate matches
 - if (certificate.SubjectName.Name !=
 
ConfigurationManager.AppSettings["CertName"])- throw new SecurityTokenValidationException(
 
"Certificated was not issued by thrusted issuer");- }
 - }
 - }
 
你可以把Validate方法里面留空讓所有的認證都通過,也可以自己定義認證邏輯,如果認證失敗,就拋出'SecurityTokenValidationException'的異常,然后我們配置一下客戶端的webconfig讓它使用我們自己的X509認證,增加以下的配置節(jié),并在'endpoint'節(jié)中指定behaviorConfiguration="myClientBehavior"。
- < behaviors>
 - < endpointBehaviors>
 - < behavior name="myClientBehavior">
 - < clientCredentials>
 - < serviceCertificate>
 - < authentication certificateValidationMode=
 
"Custom" customCertificateValidatorType=
"ClientWeb.CustomX509Validator.
MyX509Validator,ClientWeb" />- < /serviceCertificate>
 - < /clientCredentials>
 - < /behavior>
 - < /endpointBehaviors>
 - < /behaviors>
 
OK,客戶端代碼和配置完成,現(xiàn)在你可以運行自己的WCF用戶密碼認證程序了。















 
 
 

 
 
 
 