WCF +クロスドメインAjax呼び出し(CORS)+承認

こんにちは

さまざまなドメインのWCFサービスを操作する問題を解決するための可能なアプローチの1つを示したいと思います。 このトピックで見つけた情報は不完全であるか、理解しにくい情報が多すぎたためです。 Cookieや承認に関する情報など、WCFとAJAX POSTリクエスト間の相互作用のいくつかの方法についてお話したいと思います。



ご存じのように、セキュリティ上の理由により、別のドメインへのAJAX呼び出しは機能しません。 この問題を解決するために、CORS標準( wikimozilla )が発明され、リリースされました。 この標準は、特定のHTTPヘッダーを使用してアクセスを許可および制限することを意味します。 このプロトコルを使用した簡略化された通信プロセスは、以下を意味します。



クライアント(ブラウザー)はOrigin



HTTPヘッダーとの接続を開始します。サーバーはAccess-Control-Allow-Origin



ヘッダーを使用して応答する必要があります。 アドレスからのリクエスト/レスポンスのペアの例 foo.example



サービスへの foo.example



bar.other/resources/public-data



bar.other/resources/public-data





リクエスト:

GET /リソース/パブリックデータ/ HTTP / 1.1

ホスト:bar.other

起源: foo.example





[その他のヘッダー]



答えは:

HTTP / 1.1 200 OK

日付:2008年12月1日月曜日00:23:53 GMT

Access-Control-Allow-Origin:*

Content-Type:アプリケーション/ xml



[XMLデータ]





見出し




一般に、ブラウザは制限を課します。 ヘッダーの内容が気に入らない場合、ユーザーにこのデータを提供しません(必要なAccess-Control-Allow-Headers



が返されない限り、またはAccess-Control-Allow-Credentials



と正しいAccess-Control-Allow-Origin



指定されAccess-Control-Allow-Origin



いない場合はサーバーに送信します) Access-Control-Allow-Origin



:別のドメインへのPOST



リクエストの前に、ブラウザは最初にプリフライトリクエストを行い、許可されたサービス操作方法に関する情報を取得します。



WCF


このトピックには、さまざまな品質の一定量の情報があります。 残念ながら、WCFではこれらのヘッダーを標準的な方法で使用することはできませんが、この問題を解決するためのオプションがいくつかあります。 それらのいくつかをあなたの注意を喚起します。



web.configを使用したソリューション。


このソリューションでは、web.configに必要なヘッダーを直接追加します。

 <system.webServer> <httpProtocol> <customHeaders> <add name="Access-Control-Allow-Origin" value="http://foo.example" /> <add name="Access-Control-Allow-Headers" value="Content-Type" /> <add name="Access-Control-Allow-Methods" value="POST, GET, OPTIONS" /> <add name="Access-Control-Allow-Credentials" value="true" /> </customHeaders> </httpProtocol> </system.webServer>
      
      





それは、そのシンプルさと柔軟性のなさで際立っています。 特に、この特定の例は、可能性のあるドメインが複数ある場合は使用できません。さらに、サイト全体(特定の場合)へのCORSを許可します。



Global.asaxを使用したソリューション


このソリューションでは、必要なヘッダーを各リクエストに追加するコードをGlobal.asax.csで作成します。

 protected void Application_BeginRequest(object sender, EventArgs e) { var allowedOrigins = new [] { "http://foo.example", "http://bar.example" }; var request = HttpContext.Current.Request; var response = HttpContext.Current.Response; var origin = request.Headers["Origin"]; if (origin != null && allowedOrigins.Any(x => x == origin)) { response.AddHeader("Access-Control-Allow-Origin", origin); response.AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); response.AddHeader("Access-Control-Allow-Headers", "Content-Type, X-Requested-With"); response.AddHeader("Access-Control-Allow-Credentials", "true"); if (request.HttpMethod == "OPTIONS") { response.End(); } } }
      
      





このソリューションは複数のドメインをサポートしますが、サイト全体に適用されます。 もちろん、特定のサービスのすべての条件をその場で設定できますが、私の意見では、許可されたサービスのリストを維持するのに不便です。



WCFサービスコードにヘッダーを追加するソリューション


このソリューションは、特定のサービスまたはメソッドにヘッダーが追加されるという点でのみ、以前のソリューションと異なります。 一般的に、ソリューションは次のようになります。

 [ServiceContract] public class MyService { [OperationContract] [WebInvoke(Method = "POST", ...)] public string DoStuff() { AddCorsHeaders(); return "<Data>"; } private void AddCorsHeaders() { var allowedOrigins = new [] { "http://foo.example", "http://bar.example" }; var request = WebOperationContext.Current.IncomingRequest; var response = WebOperationContext.Current.OutgoingResponse; var origin = request.Headers["Origin"]; if (origin != null && allowedOrigins.Any(x => x == origin)) { response.AddHeader("Access-Control-Allow-Origin", origin); response.AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); response.AddHeader("Access-Control-Allow-Headers", "Content-Type, X-Requested-With"); response.AddHeader("Access-Control-Allow-Credentials", "true"); if (request.HttpMethod == "OPTIONS") { response.End(); } } } }
      
      





このアプローチにより、サービスのフレームワークまたはメソッドでのCORSの使用を制限できます。 主な欠点は、すべてのサービスメソッドでAddCorsHeaders



を呼び出す必要AddCorsHeaders



です。 プラス-使いやすさ。



ネイティブEndPointBehaviorとDispatchMessageInspectorを使用したソリューション


このアプローチでは、WCFの機能を使用して機能を拡張します。

EnableCorsBehavior



2つのクラスが作成されます。

 using System; using System.ServiceModel.Channels; using System.ServiceModel.Configuration; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; namespace My.Web.Cors { public class EnableCorsBehavior : BehaviorExtensionElement, IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new EnableCorsMessageInspector()); } public void Validate(ServiceEndpoint endpoint) { } public override Type BehaviorType { get { return typeof(EnableCorsBehavior); } } protected override object CreateBehavior() { return new EnableCorsBehavior(); } } }
      
      





およびEnableCorsMessageInspector





 using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Dispatcher; namespace My.Web.Cors { public class EnableCorsMessageInspector : IDispatchMessageInspector { public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) { var allowedOrigins = new [] { "http://foo.example", "http://bar.example" }; var httpProp = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name]; if (httpProp != null) { string origin = httpProp.Headers["Origin"]; if (origin != null && allowedOrigins.Any(x => x == origin)) { return origin; } } return null; } public void BeforeSendReply(ref Message reply, object correlationState) { string origin = correlationState as string; if (origin != null) { HttpResponseMessageProperty httpProp = null; if (reply.Properties.ContainsKey(HttpResponseMessageProperty.Name)) { httpProp = (HttpResponseMessageProperty)reply.Properties[HttpResponseMessageProperty.Name]; } else { httpProp = new HttpResponseMessageProperty(); reply.Properties.Add(HttpResponseMessageProperty.Name, httpProp); } httpProp.Headers.Add("Access-Control-Allow-Origin", origin); httpProp.Headers.Add("Access-Control-Allow-Credentials", "true"); httpProp.Headers.Add("Access-Control-Request-Method", "POST,GET,OPTIONS"); httpProp.Headers.Add("Access-Control-Allow-Headers", "X-Requested-With,Content-Type"); } } } }
      
      





作成したEnableCorsBehavior



web.config



追加します。

 <system.serviceModel> ... <extensions> <behaviorExtensions> <add name="crossOriginResourceSharingBehavior" type="My.Web.Cors.EnableCorsBehavior, My.Web, Version=1.0.0.0, Culture=neutral" /> </behaviorExtensions> </extensions> ... </system.serviceModel>
      
      





EnableCorsBehavior



用に作成された拡張機能をEnableCorsBehavior



て、 Endpoint



Behavior



構成に追加します

 <system.serviceModel> <services> <service name="My.Web.Services.MyService"> <endpoint address="" behaviorConfiguration="My.Web.Services.MyService" binding="webHttpBinding" contract="My.Web.Services.MyService" /> </service> </services> ... <behaviors> ... <endpointBehaviors> ... <behavior name="My.Web.Services.MyService"> <webHttp/> <crossOriginResourceSharingBehavior /> <!--     --> </behavior> ... </endpointBehaviors> ... </behaviors> ... </system.serviceModel>
      
      





OPTIONS



メソッドで予備リクエストを処理するだけです。 私の場合、最も単純なオプションを使用しましたOPTIONS



要求ハンドラーメソッドがサービスの本体に追加されます。

 [OperationContract] [WebInvoke(Method = "OPTIONS", UriTemplate = "*")] public void GetOptions() { //    EnableCorsMessageInspector }
      
      





もちろん、プリフライトリクエストを処理するための同様のWCF拡張機能があります。そのうちの1つは、記事の最後にある参照リストから参照して読み取ることができます。 主な欠点は、サービスの本体にGetOptions



メソッドを追加する必要があることと、かなりの量の追加コードです。 一方、このアプローチにより、サービスのロジックと通信のロジックをほぼ完全に分離できます。



Javascriptとブラウザに関するいくつかの言葉


承認およびCookieデータの送信をサポートするには、 withCredentials



フラグをtrue



に設定する必要があります。 多くの人がjQueryを使用してAJAXを操作していると思うので、例を示します。

  $.ajax({ type: 'POST', cache: false, dataType: 'json', xhrFields: { withCredentials: true }, contentType: 'application/json; charset=utf-8', url: options.serviceUrl + '/DoStuff' });
      
      







残念ながら、認証データの送信に関連する機能は、バージョン10以降のIEでのみ使用可能になりました。IE8/ 9ブラウザーはこの情報の送信をサポートせず、GETおよびPOSTでのみ機能します。



ログイン


上記のすべてのアプローチでは、メインサイトからの認証データが暗黙的に使用されます。 メインサイトでの承認 bar.other



bar.other



、サイトからのAjaxリクエストによりユーザーデータを返すことができます foo.example



foo.example



私の場合、これはユーザーが同じビジネスプロジェクトの一部として存在しているが、異なるドメインとプラットフォームにあるサイトの1つにいる間に、通知を受信して​​イベントに応答できるようにするために使用されました。 前述のように、ここで重要なのはヘッダーAccess-Control-Allow-Credentials



XmlHttpRequest



フラグwithCredentials=true



設定withCredentials=true







ソースのリスト





All Articles