摘要:用一個簡單範例說明如何讓 WCF 服務同時支援 HTTP 和 HTTPS。
工具:Visual Studio 2012 with Update 1
Step 1. 建立 WCF 服務
操作:
如果是 HTTP,這樣的預設組態就能用了,WCF 用戶端應用程式只要加入此服務參考,便可順利呼叫服務。但我們想要使用 HTTPS,而且是同時支援 HTTP 和 HTTPS,此預設組態當然就不夠用了。
要讓這個 WCF 服務接受 HTTP 和 HTTPS 請求,可在 web.config 的 <system.serviceModel> 區段裡面加上兩個 endpoint 元素和兩個 binding 元素,分別定義 HTTP 和 HTTPS 的端點與繫結參數。範例如下:
注意兩個 binding 參數的差異:HttpBinding 的安全模式(security 元素的 model 屬性)為 "None",而 HttpsBinding 的安全模式為 "Transport"。如果將兩個 binding 的安全模式設為相同,例如二者皆為 "Transport",在存取服務時會出現錯誤訊息:
A binding instance has already been associated to listen URI 'https://michael-a43s/MyService.svc'. If two endpoints want to share the same ListenUri, they must also share the same binding object instance. The two conflicting endpoints were either specified in AddServiceEndpoint() calls, in a config file, or a combination of AddServiceEndpoint() and config.
還有一個要注意的地方,是當我們使用 IIS Express 作為開發時期的伺服器時,必須把 ASP.NET 應用程式專案的 SSL Enabled 屬性改為 True。你可以在 Solution Explorer 中點選專案名稱,然後到屬性視窗中設定 SSL Enabled 屬性。參考下圖:
少了此動作,在 web.config 中加入前述組態(支援 HTTP 和 HTTPS)之後,用瀏覽器存取 WSDL 時就會出現錯誤訊息:
Could not find a base address that matches scheme https for the endpoint with binding BasicHttpsBinding. Registered base address schemes are [http].
Step 2. 部署 WCF 服務
部署到本機 IIS,站台名稱取名 WcfDemo,繫結 80 和 443 port。如下圖:
在建立 HTTPS 繫結時必須指定 SSL 憑證。在此範例中,我用的是 IIS Express 開發憑證:
試以瀏覽器查看 WSDL 文件,網址為 http://localhost/MyService.svc?wsdl。結果應該會先看到如下圖的警告:
選擇「繼續瀏覽此網站」之後,就會顯示 WSDL。注意裡面有兩個 <wsdl:port> 元素,可以看出 MyService 同時支援 HTTP 和 HTTPS:
Step 3:撰寫 WCF 用戶端程式
操作:
與伺服器端的 web.config 類似,此用戶端應用程式組態檔中也有兩組 binding 和 endpoint,分別設定 HTTP 和 HTTPS 的參數。不同的地方是這裡的兩個 endpoint 元素都有指定 name 參數,以便程式在建立 proxy 物件時能夠明確指定使用哪一個 endpoint。
為了方便測試,我在 form 上面放一個 ComboBox,並預先把兩個 endpoint 的名稱塞給 ComboBox 的 Items 集合,參考下圖:
接著撰寫按鈕 "Call MySevice" 點擊事件的處理常式:
執行看看。首先選擇 BasicHttpBinding_IMyService,這應該沒問題。接著改用 SecureHttpBinding_IMyService,結果按下 "Call MyService" 按鈕之後卻出現錯誤:
無法利用授權 'michael-a43s' 為 SSL/TLS 安全通道建立信任關係。
英文訊息是 "Could not establish trust relationship for the SSL/TLS secure channel with authority 'michael-a43s'"。其中 'michael-a43s' 是我的本機電腦名稱。
這是因為我在伺服器端使用的 SSL 憑證是 IIS Express 開發版憑證的緣故。試過將 IIS Express 開發憑證從個人憑證區搬到「受信任的根憑證授信單位」之下,還是出現同樣錯誤。
碰到這個狀況,一個暫時解法是強迫應用程式忽略憑證是否有效,範例如下:
這段程式碼的作用是當 ServicePointManager 檢查憑證時,若憑證主體(subject)名稱包含 "localhost" 字樣,就一律放行。註:IIS Express 開發憑證的主體名稱是 "localhost"。
正式環境通常會安裝有效的 SSL 憑證,應該就不會用到此臨時解法吧。
Trouble Shooting
有一次,我將我的 ASP.NET 應用程式部署到第二台機器之後,存取該應用程式中的 WCF 服務時出現錯誤:
Could not find a base address that matches scheme http for the endpoint with binding BasicHttpBinding. Registered base address schemes are [https].
由於另一台機器也有部署相同的應用程式,程式碼和組態檔完全相同。經過比對之後,發現兩個網站唯一的差別是第二台機器上的網站沒有設定 HTTPS 繫結。加上 HTTPS 繫結並指定一個 SSL 憑證之後,問題便消失了。
延伸閱讀
工具:Visual Studio 2012 with Update 1
Step 1. 建立 WCF 服務
操作:
- 建立新專案,專案範本選擇 ASP.NET Empty Web Application,命名為 WcfHttpsDemo。
- 加入新的 WCF 服務,命名為 MyService.svc。預設範本會幫我們產生 IMyService.cs 和 MyService.cs,裡面有個 DoWork 方法。
<configuration> <system.web> <compilation debug="true" targetFramework="4.5" /> <httpRuntime targetFramework="4.5" /> </system.web> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior name=""> <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false" /> </behavior> </serviceBehaviors> </behaviors> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> </system.serviceModel> </configuration>
如果是 HTTP,這樣的預設組態就能用了,WCF 用戶端應用程式只要加入此服務參考,便可順利呼叫服務。但我們想要使用 HTTPS,而且是同時支援 HTTP 和 HTTPS,此預設組態當然就不夠用了。
要讓這個 WCF 服務接受 HTTP 和 HTTPS 請求,可在 web.config 的 <system.serviceModel> 區段裡面加上兩個 endpoint 元素和兩個 binding 元素,分別定義 HTTP 和 HTTPS 的端點與繫結參數。範例如下:
<services> <service name="WcfHttpsDemo.MyService"> <endpoint address="" binding="basicHttpBinding" bindingConfiguration="HttpBinding" contract="WcfHttpsDemo.IMyService" /> <endpoint address="" binding="basicHttpsBinding" bindingConfiguration="HttpsBinding" contract="WcfHttpsDemo.IMyService" /> </service> </services> <bindings> <basicHttpBinding> <binding name="HttpBinding"> <security mode="None"> <transport clientCredentialType="None"></transport> </security> </binding> </basicHttpBinding> <basicHttpsBinding> <binding name="HttpsBinding"> <security mode="Transport"> <transport clientCredentialType="None"></transport> </security> </binding> </basicHttpsBinding> </bindings>
注意兩個 binding 參數的差異:HttpBinding 的安全模式(security 元素的 model 屬性)為 "None",而 HttpsBinding 的安全模式為 "Transport"。如果將兩個 binding 的安全模式設為相同,例如二者皆為 "Transport",在存取服務時會出現錯誤訊息:
A binding instance has already been associated to listen URI 'https://michael-a43s/MyService.svc'. If two endpoints want to share the same ListenUri, they must also share the same binding object instance. The two conflicting endpoints were either specified in AddServiceEndpoint() calls, in a config file, or a combination of AddServiceEndpoint() and config.
還有一個要注意的地方,是當我們使用 IIS Express 作為開發時期的伺服器時,必須把 ASP.NET 應用程式專案的 SSL Enabled 屬性改為 True。你可以在 Solution Explorer 中點選專案名稱,然後到屬性視窗中設定 SSL Enabled 屬性。參考下圖:
少了此動作,在 web.config 中加入前述組態(支援 HTTP 和 HTTPS)之後,用瀏覽器存取 WSDL 時就會出現錯誤訊息:
Could not find a base address that matches scheme https for the endpoint with binding BasicHttpsBinding. Registered base address schemes are [http].
Step 2. 部署 WCF 服務
部署到本機 IIS,站台名稱取名 WcfDemo,繫結 80 和 443 port。如下圖:
在建立 HTTPS 繫結時必須指定 SSL 憑證。在此範例中,我用的是 IIS Express 開發憑證:
試以瀏覽器查看 WSDL 文件,網址為 http://localhost/MyService.svc?wsdl。結果應該會先看到如下圖的警告:
選擇「繼續瀏覽此網站」之後,就會顯示 WSDL。注意裡面有兩個 <wsdl:port> 元素,可以看出 MyService 同時支援 HTTP 和 HTTPS:
Step 3:撰寫 WCF 用戶端程式
操作:
- 建立一個 Windows Forms 應用程式專案。
- 加入服務參考,位址輸入 http://localhost/MyService.svc。命名空間指定為 "Demo"。
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <system.serviceModel> <bindings> <basicHttpBinding> <binding name="BasicHttpBinding_IMyService"> <security mode="None"> <transport clientCredentialType="None"></transport> </security> </binding> </basicHttpBinding> <basicHttpsBinding> <binding name="BasicHttpsBinding_IMyService"> <security mode="Transport"> <transport clientCredentialType="None"></transport> </security> </binding> </basicHttpsBinding> </bindings> <client> <endpoint name="BasicHttpBinding_IMyService" contract="Demo.IMyService" address="http://michael-a43s/MyService.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IMyService" /> <endpoint name="SecureHttpBinding_IMyService" contract="Demo.IMyService" address="https://michael-a43s/MyService.svc" binding="basicHttpsBinding" bindingConfiguration="BasicHttpsBinding_IMyService" /> </client> </system.serviceModel> </configuration>
與伺服器端的 web.config 類似,此用戶端應用程式組態檔中也有兩組 binding 和 endpoint,分別設定 HTTP 和 HTTPS 的參數。不同的地方是這裡的兩個 endpoint 元素都有指定 name 參數,以便程式在建立 proxy 物件時能夠明確指定使用哪一個 endpoint。
為了方便測試,我在 form 上面放一個 ComboBox,並預先把兩個 endpoint 的名稱塞給 ComboBox 的 Items 集合,參考下圖:
接著撰寫按鈕 "Call MySevice" 點擊事件的處理常式:
private void button1_Click(object sender, EventArgs e) { var client = new Demo.MyServiceClient(comboBox1.Text); string s = client.DoWork("Michael"); MessageBox.Show(s); }
執行看看。首先選擇 BasicHttpBinding_IMyService,這應該沒問題。接著改用 SecureHttpBinding_IMyService,結果按下 "Call MyService" 按鈕之後卻出現錯誤:
無法利用授權 'michael-a43s' 為 SSL/TLS 安全通道建立信任關係。
英文訊息是 "Could not establish trust relationship for the SSL/TLS secure channel with authority 'michael-a43s'"。其中 'michael-a43s' 是我的本機電腦名稱。
這是因為我在伺服器端使用的 SSL 憑證是 IIS Express 開發版憑證的緣故。試過將 IIS Express 開發憑證從個人憑證區搬到「受信任的根憑證授信單位」之下,還是出現同樣錯誤。
碰到這個狀況,一個暫時解法是強迫應用程式忽略憑證是否有效,範例如下:
private void Form1_Load(object sender, EventArgs e) { ServicePointManager.ServerCertificateValidationCallback = ( object s, System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors) >= { if (certificate.Subject.Contains("localhost")) { return true; } return false; }; }
這段程式碼的作用是當 ServicePointManager 檢查憑證時,若憑證主體(subject)名稱包含 "localhost" 字樣,就一律放行。註:IIS Express 開發憑證的主體名稱是 "localhost"。
正式環境通常會安裝有效的 SSL 憑證,應該就不會用到此臨時解法吧。
Trouble Shooting
有一次,我將我的 ASP.NET 應用程式部署到第二台機器之後,存取該應用程式中的 WCF 服務時出現錯誤:
Could not find a base address that matches scheme http for the endpoint with binding BasicHttpBinding. Registered base address schemes are [https].
由於另一台機器也有部署相同的應用程式,程式碼和組態檔完全相同。經過比對之後,發現兩個網站唯一的差別是第二台機器上的網站沒有設定 HTTPS 繫結。加上 HTTPS 繫結並指定一個 SSL 憑證之後,問題便消失了。
延伸閱讀
沒有留言: