最近做WCF開發,有個需求是在服務端做認證,網上查資料了解到可以用UserName和Password
來做認證,隻需要寫好配置檔案和在服務端寫好驗證類就行了,但是網上普遍的博文都是需要用證書,而我自己卻隻想做個簡單的認證不想用證書來增加傳輸的安全性。随後在網上浏覽了很久都沒有找到現成的例子,最後明白靠人不如靠己啊哈哈

,于是決定自己好好研究下WCF的認證體系。
這裡特别感謝蔣金楠老師的部落格
http://www.cnblogs.com/artech/archive/2011/05/22/authentication_01.html
這篇博文從WCF的兩種安全模式談起,講到WCF的安全體系主要包括三個方面:傳輸安全(Transfer Security)、授權或者通路控制(Authorization OR Access Control)以及稽核(Auditing)。而傳輸安全又包括兩個方面:認證(Authentication)和消息保護(Message
Protection)。認證幫助用戶端或者服務确認對方的真實身份,而消息保護則通過簽名和加密實作消息的一緻性和機密性。WCF采用兩種不同的機制來解決這三個涉及到傳輸安全的問題,我們一般将它們稱為不同的安全模式,即Transport安全模式和Message安全模式。 這篇博文詳細介紹了Transport ,Message 以及他們兩種混合的Mixed模式,有興趣的童鞋可以看看。
而我今天的任務就是要仔細研究下 在Transport模式和Message模式下的具體應用的異同,以便來實作上文所說的傳輸安全。
對于伺服器,如果我們做傳輸安全的功能需要添加Binding的安全性設定和設定具體的伺服器認證方式。如下,在配置檔案中的設定:
<behavior name="metadataBehavior">
<serviceCredentials>
<userNameAuthentication
userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="WCFTestService.CustomValidator, WCFTestService"/>
</serviceCredentials>
</behavior>
<binding name="basicHttpBinding" receiveTimeout="00:10:00">
<security
mode="TransportCredentialOnly">
<transport clientCredentialType="Basic"></transport>
</security>
</binding>
Binding的安全性設定
如上的配置檔案中在我們的binding的元素屬性中有個security的節點,我們在這個節點設定mode的值則是設定安全方式。WCF
提供了 5 種不同的安全方式:
None: 不采取任何安全措施,僅适合在内部安全環境使用。
Transport: 在傳輸協定級别上對通道的所有通訊進行加密,可使用的通訊協定包括 HTTPS、TCP、IPC 和 MSMQ。優點是應用廣泛,多平台支援,實施友善簡單,效率極高,适合高吞吐量的服務使用;缺點是隻能實作點對點(point-to-point)的消息安全,在使用中介連接配接(Proxy)時可能會洩漏消息内容,比較适用于于
Intranet 或直接連接配接的環境。
Message: 通過相關标準(如 WS-Security)直接對消息進行加密來達到安全目的。優點是能實作端到端(end-to-end)的安全傳輸,不存在中介安全隐患,且擴充性較好。因采取工業安全标準,是以整合能力更強,适用于
Internet 服務。缺點是比 Transport 效率要低一些。
Mixed(TransportWithMessageCredential): 混合了上面兩種方式。使用 Transport 方式完成消息完整性、消息機密性以及伺服器認證,而使用 Message 方式完成用戶端認證。
Both: 使用 Transport 和 Message 共同完成所有的安全過程,比較恐怖,性能低下,隻有 NetMsmqBinding 支援這一安全方式
下圖列出了各種類型bind對這5種安全方式的支援:
而對于BasicHttpBinding還具有一種特殊的方式TransportCredentialOnly,它隻提供針對于HTTP的用戶端認證,并不能提供消息一緻性和機密性的保證。(當看到這種方式我就覺得應該是最滿足于我的了)。
注:在我的測試中當我設定方式為Transport在服務端出錯了,日志檔案顯示“提供的
URI 方案“http”無效,應為“https”。”,查閱資料才知道由于所有基于HTTP的綁定都通過HTTPS來實作Transport安全,是以當選擇Transport和TransportWithMessageCredential安全模式的情況下,終結點位址必須是一個HTTPS位址,而我自己的終結點位址是設定的http。
安全性解決了消息的完整性和機密性,那麼剩下來的就是認證 了。WCF 支援多種認證方式,允許我們從多個 "用戶端驗證類型(Client Credentials Types)" 中選擇适合我們需求的方案,比如經典的 "使用者名/密碼",或者
"Windows 內建身份認證(NTLM 或 Kerberos)"、"X.509 數字證書" 等。 接下來我們來看看用戶端憑證,這也就是對應在security節點下的元素節點,分為message和transport。
基于Transport的用戶端憑證
我們需要設定clientCredentialType 來确定用戶端憑證的類型,它擁有六種不同的用戶端使用者憑證類型展現了服務端針對用戶端不同的認證方式:
None:用戶端無需指定使用者憑證,即匿名認證。此為預設值;
Basic:采用Basic認證方式進行用戶端認證。在這種認證方式下,用戶端需要提供有效的使用者名和密碼,但是僅僅采用較弱的方式對密碼進行加密。是以當且僅當你确定用戶端和服務端之間的連接配接絕對安全的前提下,你才能用這種認證方式;
Digest:采用Digest認證方式進行用戶端認證。Digest認證提供與Basic一樣的認證功能,但是在安全性上有所提升。主要展現并不是直接将使用者名和密碼直接進行網絡傳輸,而是對其進行哈希計算(MD5)得到一個哈希碼(此過程又稱為Message
Digest),最終傳輸的是該哈希碼;
Ntlm:表示使用基于NTLM方式的Windows內建認證方式對用戶端進行認證;
Windows:表示使用Windows內建認證的方式對用戶端進行認證。如果能夠使用Kerberos,則直接采用Kerberos進行認證,否則才使用NTLM;
Certificate:表示用戶端的身份通過一個X.509數字證書表示,服務端通過校驗證書的方式來确定用戶端的真實身份。
基于Message模式的用戶端憑證
我們需要設定clientCredentialType
來确定用戶端憑證的類型,它擁有兩種不同的用戶端使用者憑證類型展現了服務端針對用戶端不同的認證方式UserName和Certificate
它們分别代表基于使用者名/密碼的憑證和針對X.509證書的憑證。在預設的情況下采用使用者名/密碼的憑證。
關于上述的兩種用戶端憑證,UserName隻能用在Mixed模式下。當你選擇了Message模式,則隻能選擇Certificate。
總結
放上兩張圖,第一張是系統預定義綁定類型對transport用戶端憑證的支援,第二張是系統預定義綁定類型對message用戶端憑證的支援
使用者名/密碼認證模式
在前文提到過,認證模式對于Transport和Message有稍許的不同,但都包括windows,UserName和Certificate,因為我的目的就是用UserName來解決問題,是以就隻嚴爵了下UserName的模式。
使用者名/密碼憑證在用戶端的設定很容易,但是我們關心的是服務端采用怎樣的機制來驗證這個憑證。WCF為你提供了如下三種方式來驗證憑證中使用者名是否和密碼相符:
Windows:将使用者名和密碼映射為Windows帳号和密碼,采用Windows認證;
MembershipProvider:利用配置的MembershipProvider驗證使用者名和密碼;
自定義:通過繼承抽象類UsernamePasswordValidator,自定義使用者名/密碼驗證器進行驗證。
我采用的是自定義,繼承抽象類UsernamePasswordValidator,重寫方法Validate來自定義驗證規則,最後在配置檔案中behavior節點的元素屬性中配置
<span style="font-size:18px;"><serviceCredentials>
<userNameAuthentication
userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="WCFTestService.CustomValidator, WCFTestService"/>
</serviceCredentials></span>
customUserNamePasswordValidatorType 包括兩部分前面是自定義驗證類的完全限定名,中間是逗号,最後是命名空間。
最後則是來完成自己的目标了,我設定security mode為TransportCredentialOnly ,然後用基于transport模式的用戶端憑證,clientCredentialType設定為“Basic”,具體配置可以參見文章開頭。OK 伺服器大功告成。
剩下的就是用戶端了,用戶端來說相對要簡單的多,配置檔案中添加
<span style="font-size:18px;"><span style="font-family:FangSong_GB2312;"> <binding name="basicBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic"/>
</security>
</binding></span></span>
用戶端調用方面需要提供UserName和Password
<span style="font-size:18px;"><span style="font-family:FangSong_GB2312;">using (ChannelFactory<IService> channelFactory = new ChannelFactory<IService>("service"))
{
IService proxy = channelFactory.CreateChannel();
UserNamePasswordClientCredential credential = channelFactory.Credentials.UserName;
credential.UserName = "Admin";
credential.Password = "123456"; </span></span>
終于寫完了,也算對這一天工作的一個總結。接下來我會寫一點關于伺服器契約方面的問題,記錄我工作當中遇到的問題,還有WCF伺服器診斷。