閱讀776 返回首頁    go 阿裏雲 go 技術社區[雲棲]


[WCF安全係列]綁定、安全模式與客戶端憑證類型:BasicHttpBinding

整個安全傳輸是在WCF的信道層進行的,而綁定是信道層的締造者,所以終結點采用哪種類型的綁定以及對綁定的屬性進行怎樣的設置決定了信道層最終采用何種機製實現消息的安全傳輸。具體來說,我們可以通過綁定設置最終采用的安全模式,以及基於相應安全模式下進行認證和消息保護的行為。

不同的綁定類型由於其采用的傳輸協議不同,應用的場景也各有側重,很難提供一種統一的應用編程接口完成基於不同綁定的安全設置,所以每一種綁定都具有各自用於安全設置相關的類型。但是基於對安全的設置,大部分係統預定義綁定(不是所有)都具有類似於如下代碼片斷所示的屬性定義。

   1: Public class XxxBinding
   2: {
   3:     Public XxxSecurity Security {}
   4: }
   5: Public class XxxSecurity
   6: {
   7:     Public XxxSecurityMode          Mode{get;set;}
   8:     Public XxxTransportSecurity     Transport{get;set;}
   9:     Public XxxMessageSecurity       Message{get;set;}
  10: }

對於某個綁定XxxBinding(Xxx泛指某種綁定類型,所有帶Xxx前綴的類型並不意味著它們代表完全一樣的字符),它具有一個專屬的XxxSecurity類型的Security屬性。而這個XxxSecurity類型一般具有三個屬性:Mode表示采用的安全模式,而Transport和Message用於針對Transport和Message安全模式下的設置。

對於圍繞著綁定進行的安全設置,我們首要的任務就是製定采用的安全模式。在安全模式確定之後,客戶端憑證的選擇決定了認證方最終采用怎樣的認證機製。接下來,我們就來談談針對不各種常用的係統預定義綁定,安全模式和基於安全模式的客戶端憑證如何設置。先從BasicHttpBinding談起。

下麵的代碼片斷表示BasicHttpBinding安全相關應用編程接口的定義,這和上麵給出的“泛型綁定”的定義完全一致。通過Security屬性返回用於進行針對BasicHttpBinding安全設置的類型為BasicHttpSecurity。BasicHttpSecurity的Mode、Transport和Message三種屬性的類型分別為BasicHttpSecurityModeHttpTransportSecurityBasicHttpMessageSecurity

   1: public class BasicHttpBinding : Binding, IBindingRuntimePreferences
   2: {
   3:     //其他成員
   4:     public BasicHttpSecurity Security { get; set; }
   5: }
   6: public sealed class BasicHttpSecurity
   7: {
   8:     //其他成員
   9:     public BasicHttpSecurityMode    Mode { get; set; }
  10:     public HttpTransportSecurity    Transport { get; set; }
  11:     public BasicHttpMessageSecurity Message { get; set; }
  12: }

枚舉BasicHttpSecurityMode中定義了BasicHttpBinding支持的5中安全模式。其中None為默認選項,表示並不采用任何安全機製,Transport、Message和TransportWithMessageCredential分別表示之前介紹的Transport、Message和Mixed安全模式。TransportWithMessageCredential表示“使用基於Message模式憑證的Transport模式”。由於Mixed安全模式通過Message模式實現對客戶端的認證,所以要求客戶端采用基於Message模式的憑證。而除客戶端認證的其他安全要素的實現則都是采用Transport模式。所以TransportWithMessageCredential在BasicHttpSecurityMode枚舉中的表示和我們講的Mixed模式從語義上講是一致的。TransportCredentialOnly是BasicHttpBinding所獨有的安全模式。它隻提供針對於HTTP的客戶端認證,並不能提供消息一致性和機密性的保證。

   1: public enum BasicHttpSecurityMode
   2: {
   3:     None,
   4:     Transport,
   5:     Message,
   6:     TransportWithMessageCredential,
   7:     TransportCredentialOnly
   8: }

HttpTransportSecurity用於進行針對Transport模式下的安全設置。而通過ClientCredentialType屬性,我們可以設置客戶端憑證的類型。該屬性類型為HttpClientCredentialType枚舉,定義其中的六個枚舉值表示支持的六種基於Tranport模式的客戶端憑證類型。HttpTransportSecurity和HttpClientCredentialType相關定義如下。

   1: public sealed class HttpTransportSecurity
   2: {
   3:     //其他成員
   4:     public HttpClientCredentialType ClientCredentialType {get; set; }
   5: }
   6: public enum HttpClientCredentialType
   7: {
   8:     None,
   9:     Basic,
  10:     Digest,
  11:     Ntlm,
  12:     Windows,
  13:     Certificate
  14: }

定義在枚舉類型HttpClientCredentialType中的六種不同的客戶端用戶憑證類型體現了服務端針對客戶端不同的認證方式:

  • None:客戶端無需指定用戶憑證,即匿名認證。此為默認值;
  • Basic:采用Basic認證方式進行客戶端認證。在這種認證方式下,客戶端需要提供有效的用戶名和密碼,但是僅僅采用較弱的方式對密碼進行加密。所以當且僅當你確定客戶端和服務端之間的連接絕對安全的前提下,你才能用這種認證方式;
  • Digest:采用Digest認證方式進行客戶端認證。Digest認證提供與Basic一樣的認證功能,但是在安全性上有所提升。主要體現並不是直接將用戶名和密碼直接進行網絡傳輸,而是對其進行哈希計算(MD5)得到一個哈希碼(此過程又稱為Message Digest),最終傳輸的是該哈希碼;
  • Ntlm:表示使用基於NTLM方式的Windows集成認證方式對客戶端進行認證;
  • Windows:表示使用Windows集成認證的方式對客戶端進行認證。如果能夠使用Kerberos,則直接采用Kerberos進行認證,否則才使用NTLM;
  • Certificate:表示客戶端的身份通過一個X.509數字證書表示,服務端通過校驗證書的方式來確定客戶端的真實身份。

無論是在進行服務寄宿的時候為ServiceHost添加終結點,還是在客戶端創建調用服務的終結點,都可以通過編程的方式來設置綁定的安全模式和客戶端用於憑證類型。如下麵的代碼片斷所示,我們為BasicHttpBinding設置了Transport安全模式,並將其客戶端憑證設置成Windows。由於所有基於HTTP的綁定都通過HTTPS來實現Transport安全,所以當選擇Transport和TransportWithMessageCredential安全模式的情況下,終結點地址必須是一個HTTPS地址。

服務寄宿代碼:

   1: using(ServiceHost host = new ServiceHost(typeof(CalculatorService)))
   2: {
   3:     var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
   4:     binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
   5:     host.AddServiceEndpoint(typeof(ICalculator), binding, "https://localhost/calculatorservice");
   6:     host.Open();
   7:     ...
   8: }

服務調用代碼:

   1: var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
   2: binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
   3: using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>(binding, "https://localhost/calculatorservice"))
   4: {
   5:     ICalculator calculator = channelFactory.CreateChannel();
   6:     double result = calculator.Add(1, 2);
   7:     ...
   8: }

對於包括客戶端憑證類型在內的針對綁定的安全設置,我們還是推薦采用配置的方式。基於綁定的配置節中具有一個<security>的字節點,用於進行安全相關的設置。采用的安全模式通過該節點的mode屬性設置。而給予Transport模式相關的設置則配置在<security>/<transport>配置節中,其中配置屬性clientCredentialType表示客戶端憑證類型。在下麵的給出的配置片斷中,我為寄宿的服務添加了一個采用BasicHttpBinding的終結點,該綁定的模式被設置為Transport,並采用Certificate客戶端憑證類型。

   1: <system.serviceModel>  
   2:   <bindings>
   3:     <basicHttpBinding>
   4:       <binding name="transportBinding">
   5:         <security mode="Transport">
   6:           <transport clientCredentialType="Certificate"/>
   7:         </security>
   8:       </binding>      
   9:     </basicHttpBinding>
  10:   </bindings>
  11:   <services>
  12:     <service name="Artech.WcfServices.Services.CalculatorService">
  13:       <endpoint address="https://Jinnan-PC/calculatorservice" binding="basicHttpBinding" bindingConfiguration="transportBinding" contract="Artech.WcfServices.Contracts.ICalculator" />
  14:     </service>
  15:   </services>
  16: </system.serviceModel>

類型BasicHttpSecurity用於進行針對於BasicHttpBinding關於Message安全模式的相關設置。你同樣可以通過它的ClientCredentialType屬性設置客戶端憑證類型,該屬性類型為System.ServiceModel.BasicHttpMessageCredentialType枚舉。定義在BasicHttpMessageCredentialType中的兩個枚舉值(UserName和Certificate)表示支持的兩種客戶端憑證類型,它們分別代表基於用戶名/密碼的憑證和針對X.509證書的憑證。在默認的情況下采用用戶名/密碼的憑證。BasicHttpMessageSecurity和BasicHttpMessageCredentialType相關定義如下麵的代碼片斷所示。

   1: public sealed class BasicHttpMessageSecurity
   2: {
   3:   public BasicHttpMessageCredentialType ClientCredentialType { get; set; }
   4: }
   5: public enum BasicHttpMessageCredentialType
   6: {
   7:     UserName,
   8:     Certificate
   9: }

關於上述的兩種客戶端憑證,BasicHttpMessageCredentialType.UserName隻能用在Mixed模式下。當你選擇了Message模式,則隻能選擇BasicHttpMessageCredentialType. Certificate。 WCF為什麼會具有如此一個限製,你會在後續文章中找到答案。舉個例子,我通過如下一段代碼對服務CalculatorService進行寄宿,並采用了一個采用Message模式的BasicHttpBinding。

   1: using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
   2: {
   3:     var binding = new BasicHttpBinding(BasicHttpSecurityMode.Message);
   4:     host.AddServiceEndpoint(typeof(ICalculator), binding, "https://localhost/calculatorservice");
   5:     host.Open();
   6:     ...
   7: }

當ServiceHost被開啟的時候,如下圖所示的InvalidOperationException異常被拋出來,並提示“BasicHttpBinding綁定要求 BasicHttpBinding.Security.Message.ClientCredentialType 等效於安全消息的 BasicHttpMessageCredentialType.Certificate 憑據類型。為 UserName 憑據選擇Transport 或 TransportWithMessageCredential 安全性”。實際上這個異常消息不太正確,因為Tranport模式下根本就不存在UserName憑證類型。

image

在基於綁定的配置節中,Message模式相關選項通過<security>/<message>配置節進行設置。在該配置節中,clientCredentialType屬性用於設置客戶端憑證類型。在下麵的配置片斷中,我為寄宿的服務添加了兩個采用BasicHttpBinding的終結點。其中第一個終結點的綁定為Message模式,並采用Certificate憑證。另一個終結點綁定為TransportWithMessageCredential模式,采用UserName憑證。為了保證服務寄宿的成功,我們還必須通過服務行為的形式為服務指定一個X.509證書作為服務憑證。這實際上涉及到了服務認證的話題,我們將本節後續部分進行介紹。

   1: <system.serviceModel>
   2:   <behaviors>
   3:     <serviceBehaviors>
   4:       <behavior name="serviceCertificateBehavior">
   5:         <serviceCredentials>
   6:           <serviceCertificate findValue="Jinnan-PC" x509FindType="FindBySubjectName" />
   7:         </serviceCredentials>
   8:       </behavior>
   9:     </serviceBehaviors>
  10:   </behaviors>
  11:   <bindings>
  12:     <basicHttpBinding>
  13:       <binding name="messageBinding">
  14:         <security mode="Message">
  15:           <message clientCredentialType="Certificate"/>
  16:         </security>
  17:       </binding>
  18:       <binding name="transportWithMessageCredentialBinding">
  19:         <security mode="TransportWithMessageCredential">
  20:           <message clientCredentialType="UserName" />
  21:         </security>
  22:       </binding>
  23:     </basicHttpBinding>
  24:   </bindings>
  25:   <services>
  26:     <service behaviorConfiguration="serviceCertificateBehavior" name="Artech.WcfServices.Services.CalculatorService">
  27:       <endpoint address="https://Jinnan-PC/calculatorservice1" binding="basicHttpBinding"
  28:         bindingConfiguration="messageBinding" contract="Artech.WcfServices.Contracts.ICalculator" />
  29:       <endpoint address="https://Jinnan-PC/calculatorservice2" binding="basicHttpBinding"
  30:         bindingConfiguration="transportWithMessageCredentialBinding" contract="Artech.WcfServices.Contracts.ICalculator" />
  31:     </service>
  32:   </services>
  33: </system.serviceModel>

作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-27 11:04:00

  上一篇:go  國民基金-餘額寶-對企業研發效能的探索與思考
  下一篇:go  [WCF安全係列]綁定、安全模式與客戶端憑證類型:WSHttpBinding與WSDualHttpBinding