摘要:用一個簡單範例說明如何讓 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 憑證之後,問題便消失了。
延伸閱讀






沒有留言: