WCF 自訂使用者帳號/密碼篇(四):使用者認證的WCF服務—basicHttpBinding繫結

  • 4160
  • 0
  • WCF
  • 2022-09-18

使用者認證的WCF服務—basicHttpBinding繫結

目錄:WCF 自訂使用者帳號/密碼篇

1. 建立 basicHttpUserAuth.webhost 專案

basicHttpBinding 應該是 WCF 中最常被使用的繫結模式﹐這一篇將介紹 basicHttpBinding 如何使用自訂使用者認證的WCF服務。

繼續延續之前的文章範例﹐建立一個WCF 服務的網站專案 basicHttpUserAuth.webhost ﹐將自動產生的IService.cs﹑Service.cs及Service.svc三個檔案移除﹐然後參考使用者認證的WCF服務—範例服務程式庫(WCF Library)這一篇中的 MyProducts.lib.dll 及 UserAuthorization.lib.dll。

接著加入一個文字檔﹐將檔名改為 MyProducts.svc ﹐內容只寫一行

<%@ ServiceHost Language="C#" Debug="false" Service="MyProducts.lib.ProductService"%>

 

完成上述的動作後﹐再來就是修改組態檔。先將組態檔做到以下的部分﹐在 security 的 Mode 設定成 message 看看會有什麼情況。

<system.serviceModel>
     <services>
        <service behaviorConfiguration="Products.Behavior" name="MyProducts.lib.ProductService">
            <endpoint binding="basicHttpBinding" bindingConfiguration="Products.BasicHttpBinding"
                contract="MyProducts.lib.IProductService" />
        </service>
   </services>
    <bindings>
        <basicHttpBinding>
            <binding name="Products.BasicHttpBinding">
                <security mode="Message" />
            </binding>
        </basicHttpBinding>
    </bindings>

  <behaviors>
    <serviceBehaviors>
      <behavior name="Products.Behavior">
        <serviceMetadata httpGetEnabled="true" />
        <serviceDebug includeExceptionDetailInFaults="false" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

將上述組態存檔後以瀏覽器執行MyProducts.svc﹐出現了如下的錯誤。一般使用basicHttpBinding是搭配Transport(傳輸層)的加密方式﹐如果一定要使用Message則必須改為混搭風格使用TransportWithMessageCredential

image

現在將security的mode改為Transport﹐再將組態檔做一些修改﹐以下特別標示出的地方是新加入的設定。

image

Security之下的transport的clientCredentialType屬性必須為None。而在behavior中也必須加入憑證的設定。

然後再開啟瀏覽器執行一次MyProducts.svc。

image

這是因為basicHttpBinding使用Transport的安全性必須使用 https﹐所以這裏要變更IIS的設定﹐請參考第 2 小節的 IIS SSL設定。

假設IIS設定已完成﹐再開啟瀏覽器執行﹐這時看到的錯誤已不同﹐因為我們必須變更使用https才對。

image

網址改為 https://localhost:853/basicHttpUserAuth.webhost/MyProducts.svc 因為不是用標準的443 port﹐所以網址上必須將port number寫出。

再次執行後﹐畫面出現了警告﹐暫時先不管這個警告﹐按下畫面上的[繼續瀏覽此網站(不建議)]﹐可以看到畫面像是我們所要的。

image

image

那麼可以得到WSDL了嗎?如果點下了 https://localhost:853/basicHttpUserAuth.webhost/MyProducts.svc?wsdl 會發現不能如想像般顯示出WSDL的定義資料。這裏需要再修改WCF的組態檔。在behavior的serviceMetadata中需要再加入一個 httpsGetEnabled=true 的屬性值。

<behavior name="Products.Behavior">
    <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
    <serviceDebug includeExceptionDetailInFaults="false" />
    <serviceCredentials>
       <serviceCertificate findValue="MyWCFCert" 
                     storeLocation="LocalMachine"
                       storeName="My" 
                       x509FindType="FindBySubjectName" />
    </serviceCredentials>
</behavior>

完成設定後﹐再一次執行﹐就可以得到想要的畫面了。

image

 

2. IIS SSL 設定

因為basicHttpBinding使用傳輸層加密(Transport)必須使用https﹐因此需要設定SSL。開啟IIS之後在站台使用滑鼠右鍵﹐選擇「編輯繫結」

image

在站台繫結畫面上按下[新增]鍵﹐將類型選擇 https﹐連接埠預設為443﹐我故意另設別的port 853﹐接著在SSL憑證之處選擇所要使用的憑證。確定後就多了一個繫結設定。

image

image

 

上述設定完成後﹐回到IIS 畫面﹐接著點選我們的應用程式basicHttpUserAuth.webhost﹐在右側點選[SSL設定]。

image

[需要SSL]的核取方塊勾選﹐然後套用﹐IIS的設定到此結束。

image

 

3. basicWinClientForUserValidator 測試程式

建立一個新的WinForm程式專案﹐basicWinClientForUserValidator﹐參考上述的basicHttpBinding的WCF服務https://localhost:853/basicHttpUserAuth.webhost/MyProducts.svc?wsdl 。

程式的功能和寫法都和之前的4.4.2小節的程式差不多﹐因此直接看app.config和程式內容。

App.config

<system.serviceModel>
    <bindings>
        <basicHttpBinding>
            <binding name="BasicHttpBinding_IProductService">
                <security mode="Transport">
                    <message clientCredentialType="UserName" />
                </security>
            </binding>
        </basicHttpBinding>
    </bindings>
    <client>
        <endpoint address="https://localhost:853/basicHttpUserAuth.webhost/MyProducts.svc"
                  binding="basicHttpBinding"
                  bindingConfiguration="BasicHttpBinding_IProductService"
                  contract="ServiceHost.IProductService"
                  name="BasicHttpBinding_IProductService" />
</client>
</system.serviceModel>

Form1.cs

   1:  namespace basicWinClientForUserValidator {
   2:      public partial class Form1 : Form {
   3:          public Form1() {
   4:              InitializeComponent();
   5:          }
   6:  
   7:          private void btnSaySomething_Click(object sender, EventArgs e) {
   8:              txtException.Text = "";
   9:              txtOutput.Text = "";
  10:  
  11:              ServiceHost.ProductServiceClient proxy = null;
  12:              try{
  13:                  proxy = new basicWinClientForUserValidator.ServiceHost.ProductServiceClient();
  14:                  proxy.ClientCredentials.UserName.UserName = txtUid.Text;
  15:                  proxy.ClientCredentials.UserName.Password = txtPwd.Text;
  16:                  proxy.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
  17:  
  18:                  txtOutput.Text = proxy.SaySomething(txtInput.Text);
  19:              }catch(Exception er){
  20:                  txtException.Text = er.InnerException.Message;
  21:                  //txtException.Text = er.Message;
  22:              }
  23:          }
  24:      }
  25:  }

 

將程式執行之後﹐可惜不符合預期﹐出現了錯誤。

image

出現了”基礎連接已關閉: 無法為 SSL/TLS 安全通道建立信任關係。”錯誤訊息。這是因為Client端無法驗證Server的SSL憑證﹐因此請求失敗。現在使用的憑證是自我簽署的憑證並不安全﹐所以會檢查不通過﹐就如同IE執行WSDL服務網址跳出的此網站的安全性憑證有問題一樣。

image

可是在IE上還可以按下「繼續瀏覽此網站」﹐那是不是我們在程式上也可以忽略呢?答案是可以的﹐現在新增一個Util.cs的class。

Util.cs

   1:  namespace basicWinClientForUserValidator {
   2:      public static class Util {
   3:          public static void SetCertificatePolicy() {
   4:              ServicePointManager.ServerCertificateValidationCallback += RemoteCertificateValidate;
   5:          }
   6:  
   7:          private static bool RemoteCertificateValidate(object sender, 
   8:              X509Certificate cert, X509Chain chain, SslPolicyErrors error) {
   9:              //System.Console.WriteLine("Warning, trust any certificate.");
  10:              return true;
  11:          }
  12:      }
  13:  }

 

接著在Client程式一開始先呼叫Util.SetCertificatePolicy()就可以了。

   1:  private void btnSaySomething_Click(object sender, EventArgs e) {
   2:     txtException.Text = "";
   3:     txtOutput.Text = "";
   4:  
   5:     Util.SetCertificatePolicy();
   6:  
   7:     ServiceHost.ProductServiceClient proxy = null;
   8:     try{
   9:       proxy = new basicWinClientForUserValidator.ServiceHost.ProductServiceClient();
  10:       proxy.ClientCredentials.UserName.UserName = txtUid.Text;
  11:       proxy.ClientCredentials.UserName.Password = txtPwd.Text;
  12:       proxy.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
  13:  
  14:       txtOutput.Text = proxy.SaySomething(txtInput.Text);
  15:     }catch(Exception er){
  16:       txtException.Text = er.InnerException.Message;
  17:       //txtException.Text = er.Message;
  18:     }
  19:  }

 

把程式重新編譯後再執行﹐已經可以讓執行正確的執行了。

image

 

4. [此網站的安全性憑證有問題]﹐開發時期如何解決?

上一小節中因為憑證的安全性問題﹐所以多加了一個class去忽略憑證的檢查﹐但是如果憑證是可通過驗證的那一段是可以不需要的。可是開發期間通常就是用自我簽署的憑證﹐那麼該怎麼辦?其實只要自我簽署的憑證主體是本機的主機名稱﹐就可以通過檢查。

假設我的Server主機名稱是KEVIN-H﹐在建立憑證時其主體設為”CN=KEVIN-H”即可。

image

既然憑證換了﹐WCF的設定當然也要修改﹐所以WCF的組態檔web.config中的憑證要修改。

image

不過﹐這裏有另一個問題﹐如上圖﹐Server上憑證中有兩個主體名稱是相同的。這時如果使用FindPrivateKey工具要去找這個憑證檔﹐會得到在store中這個主體有多個相同名稱的錯誤。

image

這時必須用憑證指紋方式﹐憑證指紋可以在憑證管理中將憑證打開﹐在詳細資料中就可以找到憑證指紋﹐將一長串16位元的字串複製出來使用。注意這一串16位元的字串最開頭有一個空白字元必須去除﹐將這串直接複製到記事本時﹐最前面的空白字元會看不到﹐但其實是存在的。

image

將FrindPrivateKey指令改為如下即可

image

憑證檔找到後再利用calcls.exe賦予讀取憑證檔讀取的權限後﹐這時開啟IE執行WSDL https://localhost:853/basicHttpUserAuth.webhost/MyProducts.svc ﹐出現錯誤了

image

同樣是因為憑證主體名稱造成的﹐這時要修改web.config的設定﹐把對憑證搜尋的條件改為比較精準的方式﹐這裏同樣改為憑證指紋。

image

關於WCF自訂使用者認證的介紹到此﹐至於Java如何調用請參考另外一個文件﹐後續有可能的話再研究Android如何調用。