1. 程式人生 > >(WCF初體驗)WCF的認證和訊息保護

(WCF初體驗)WCF的認證和訊息保護

最近做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種安全方式的支援:
1
而對於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客戶端憑證的支援
2
3

使用者名稱/密碼認證模式

在前文提到過,認證模式對於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伺服器診斷。