2013-11-14 8 views
6

Ho un servizio Web WCF/SVC che viene utilizzato da una chiamata JavaScript tramite AJAX. Si accede alla pagina tramite un proxy SSL (https://gate.company.com/MyPage) ubicato nella DMZ che inoltra la richiesta al server Web interno (http://myLocalWebServer/MyPage).La callback JavaScript javascript non è supportata nei servizi autenticati. Richiesta AJAX al servizio WCF tramite proxy SSL

Dopo tanto googeling e tentativi, sono riuscito a farlo funzionare, giocando con i parametri crossDomainScriptAccessEnabled e Access-Control-Allow-Origin. Anche se funziona solo se la modalità di autenticazione è impostata su false o se l'utente non ha ancora effettuato l'accesso. Non appena la chiamata viene effettuata da una pagina in cui è necessario accedere (autenticazione dei moduli), non funziona più. Il messaggio di errore ottengo allora è:

cross domain javascript callback is not supported in authenticated services 

E funziona ancora, tuttavia, una volta logout e fare la chiamata da una pagina non protetta.

Il mio servizio assomiglia a questo

namespace MyNameSpace 
{ 
    [ServiceContract(Namespace = "MyNameSpace")] 
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] 
    public class Service 
    { 
     [OperationContract] 
     public string[] GetDropDownData(string id) 
     { 
     List<string> resultList = new List<string>(); 
     ... 
     return resultList.ToArray(); 
     } 
    } 
} 

La chiamata di servizio ed il metodo di callback in JavaScript:

function fillDropdwon(dropId){ 
    jQuery.ajax({ 
     type: "POST", 
     dataType: "jsonp", 
     contentType: "application/json; charset=utf-8", 
     cache: true, 
     url: "Service.svc/GetDropDownData", 
     data: '{"dropId":"' + dropId + '"}', 
     jsonpCallback: "onDone", 
     error: function (a,b,c) { 
      alert("error"); 
     } 
    }); 
} 

// Callback-Methode after ServiceCall 
function onDone(result) { 
    var theDropDown = jQuery("#<%= cboSelection.ClientID %>"); 
    if (theDropDown.length > 0) { 
     //Clear the old entries 
     theDropDown.empty(); 

     //Add an empty entry 
     if ("<%= cboSelection.ShowEmptyRow %>".toLowerCase() == "true") { 
      theDropDown.append($('<option></option>')); 
     } 

     // Add the found items 
     for (var i = 0; i < result.length; i++) { 
      var text = result[i]; 
      theDropDown.append($('<option></option>').val(text).html(text)); 
     } 
    } 
} 

Il web.config parte che riguarda il servizio:

<system.serviceModel> 
    <behaviors> 
    <endpointBehaviors> 
     <behavior name="MyNameSpace.ServiceAspNetAjaxBehavior"> 
     <enableWebScript /> 
     </behavior> 
    </endpointBehaviors> 
    <serviceBehaviors> 
     <behavior> 
     <serviceMetadata httpGetEnabled="true" /> 
     </behavior> 
    </serviceBehaviors> 
    </behaviors> 
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> 
    <standardEndpoints> 
    <webScriptEndpoint> 
     <standardEndpoint crossDomainScriptAccessEnabled="true" name=""/> 
    </webScriptEndpoint> 
    </standardEndpoints> 
    <services> 
    <service name="MyNameSpace.Service"> 
     <!-- Service endpoint for HTTPS --> 
     <endpoint address="" behaviorConfiguration="MyNameSpace.ServiceAspNetAjaxBehavior" binding="webHttpBinding" bindingConfiguration="jsonpBinding" contract="MyNameSpace.Service" /> --> 
    </service> 
    </services> 
    <bindings> 
    <webHttpBinding> 
     <binding name="jsonpBinding" crossDomainScriptAccessEnabled="true"> 
     <security mode="None" /> 
     </binding> 
     <binding name="jsonpSslBinding" crossDomainScriptAccessEnabled="true"> 
     <security mode="Transport" /> 
     </binding> 
    </webHttpBinding> 
    </bindings> 
</system.serviceModel> 

Per prima cosa ho provato a utilizzare il proxy AJAX di ASP.NET per chiamare il servizio, ma ciò non ha funzionato perché la chiamata w come fatto al server web direttamente, che non è SSL e l'errore che ho ottenuto era più o meno: 'Pagina https://gate.company.com/MyPage provato a caricare non salvare il contenuto dalla pagina http://myLocalWebServer/MyPage ...'. Ecco perché ho usato la chiamata AJAX sopra elencata.

function fillDropdwon(dropId){ 
    var service = new MyNameSpace.Service(); 
    service.GetDropDownData(dropId, onDone); 
} 

Ho anche cercato di aggiungere quanto segue nel web.config

<system.webServer> 
    <httpProtocol> 
    <customHeaders> 
     <!-- Enable Cross Domain AJAX calls --> 
     <remove name="Access-Control-Allow-Origin" /> 
     <add name="Access-Control-Allow-Origin" value="https://gate.company.com"/> 
    </customHeaders> 
    </httpProtocol> 
</system.webServer> 

ho controllato le intestazioni inviate al server e ho visto che quando non sono entrato, l'intestazione è simile al seguente:

Request URL:`https://gate.company.com/MyPage/Servic.svc/GetDropDownData?callback=onDone` 
Request Method:POST 
Status Code:200 OK 
Request Headersview source 
Accept:text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01 
Accept-Encoding:gzip,deflate,sdch 
Accept-Language:de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4,fr-CH;q=0.2,fr;q=0.2 
Connection:keep-alive 
Content-Length:161 
Content-Type:application/json; charset=UTF-8 
Cookie:__utma=174172730.1157990369.1360852643.1381229705.1383150435.9; __utmc=174172730; __utmz=174172730.1369635484.4.3.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=(not%20provided); promopost=oaezz3fzzj0o4l3fccxh0ss1; 
ASP.NET_SessionID= 
Host:`gate.company.com` 
Origin:`https://gate.company.com` 
Referer:`https://gate.company.com/MyPage/QuickCalculator.aspx?ObjectIdentity=47a93f52-6be6-4bd6-9600-e8eb9c8ff360` 
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.48 Safari/537.36 
X-Requested-With:XMLHttpRequest 
Query String Parametersview sourceview URL encoded 
callback:onDone 
Request Payloadview source 
{dropId:123} 
dropId: "123" 
Response Headersview source 
Cache-Control:private 
Connection:Keep-Alive 
Content-Encoding:gzip 
Content-Length:1339 
Content-Type:application/x-javascript 
Date:Sun, 01 Dec 2013 15:14:25 GMT 
Keep-Alive:timeout=15, max=97 
Server:Microsoft-IIS/7.5 
Vary:Accept-Encoding 
X-AspNet-Version:4.0.30319 
X-Powered-By 

e la risposta è simile a questa.

onDone(["result1","result2"]); 

Quando chiamo il servizio all'interno di una pagina protetta, ottengo questo:

Request URL:`https://gate.company.com/MyPage/Servic.svc/GetDropDownData?callback=onDone` 
Request Method:POST 
Status Code:200 OK 
Request Headersview source 
Accept:text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01 
Accept-Encoding:gzip,deflate,sdch 
Accept-Language:de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4,fr-CH;q=0.2,fr;q=0.2 
Connection:keep-alive 
Content-Length:161 
Content-Type:application/json; charset=UTF-8 
Cookie:__utma=174172730.1157990369.1360852643.1381229705.1383150435.9; __utmc=174172730; __utmz=174172730.1369635484.4.3.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=(not%20provided); promopost=oaezz3fzzj0o4l3fccxh0ss1; 
**ASP.NET_SessionID=; .ASPXAUTH=AB5ADCE12C7847CA452DD54D903E6787C7D1F0009B9E3277D2EC50DE9C421D1331B87A6DCA2432993933794AB9BDE833E44EC58E217D5AA1D588132C6E1C67D4AD7692840359D9A719EC2A53826CF54FDC0943B4E0AB29093920143E1E987080AC7C35E63594FD678535972D06AEC0AAF74AF8BE8DFC3746B499CB032E7771F10B924110DB344824B3253F9BECB3CDD8** 
Host:`gate.company.com` 
Origin:`https://gate.company.com` 
Referer:`https://gate.company.com/MyPage/QuickCalculator.aspx?ObjectIdentity=47a93f52-6be6-4bd6-9600-e8eb9c8ff360` 
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.48 Safari/537.36 
X-Requested-With:XMLHttpRequest 
Query String Parametersview sourceview URL encoded 
callback:onDone 
Request Payloadview source 
{dropId:123} 
dropId: "123" 
Response Headersview source 
Cache-Control:private 
Connection:Keep-Alive 
Content-Encoding:gzip 
Content-Length:1339 
Content-Type:application/x-javascript 
Date:Sun, 01 Dec 2013 15:14:25 GMT 
**jsonerror:true** 
Keep-Alive:timeout=15, max=97 
Server:Microsoft-IIS/7.5 
Vary:Accept-Encoding 
X-AspNet-Version:4.0.30319 
**X-Powered-By:ASP.NET** 

e la risposta assomiglia a questo.

onDone({"ExceptionDetail":{"HelpLink":null,"InnerException":null,"Message":"Cross domain javascript callback is not supported in authenticated services.","StackTrace":" bei System.ServiceModel.Dispatcher.JavascriptCallbackMessageInspector.AfterReceiveRequest(Message& request, IClientChannel channel, InstanceContext instanceContext)\u000d\u000a bei System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.AfterReceiveRequestCore(MessageRpc& rpc)\u000d\u000a bei System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)\u000d\u000a bei System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)","Type":"System.NotSupportedException"},"ExceptionType":"System.NotSupportedException","Message":"Cross domain javascript callback is not supported in authenticated services.","StackTrace":" bei System.ServiceModel.Dispatcher.JavascriptCallbackMessageInspector.AfterReceiveRequest(Message& request, IClientChannel channel, InstanceContext instanceContext)\u000d\u000a bei System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.AfterReceiveRequestCore(MessageRpc& rpc)\u000d\u000a bei System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)\u000d\u000a bei System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)"},500); 

La differenza principale è che c'è un SessionID e la jsonerror: true per la versione "login".

C'è un modo per risolvere questo problema?

Non è possibile "disabilitare" l'autenticazione per la richiesta AJAX cambiando l'intestazione prima della chiamata o qualcosa del genere. O c'è qualche errore nel mio codice, web.config?

Apprezzo qualsiasi suggerimento, visto che ci provo da molto tempo.

risposta

6

Ho finalmente trovato la soluzione al mio problema. Ho annotato i passaggi che ho seguito per arrivare alla soluzione e spero che questo possa aiutare chiunque abbia un problema simile.

Per prima cosa ho utilizzato il proxy AJAX di ASP.NET e ho effettuato la chiamata in questo modo.

var service = new SDAG.Post.PPT.Website.Service(); 
service.GetDropDownData(dropId, onDone); 

Tuttavia, questo non ha funzionato nella mia configurazione ambiente con l'SSL proxy che inoltra al server Web internamente tramite la porta 80 (di cui sopra). Ho ricevuto il messaggio di errore:

The page at <code>'https://gate.company.com/MyPage/Page.aspx?ObjectIdentity=f5c0c016-4828-4935-a7a9-73f3ba47a1ed'</code> was loaded over HTTPS, but displayed insecure content from <code>'http://myLocalWebServer.company.com/MyPage/Service.svc/GetDropDownData'</code>: this content should also be loaded over HTTPS. 

ScriptResource.axd?d=8mniuUQAKIvBIxCF_O9BRQpND31cf-SHqs1HBOCcP0DdxGNo4-nOZcF0WZIDoCtTdw5mZIOSt0veif…:2 

OPTIONS <code>http://myLocalWebServer.company.com/MyPage/Service.svc/GetDropDownData</code> 405 (Method Not Allowed) ScriptResource.axd?d=8mniuUQAKIvBIxCF_O9BRQpND31cf-SHqs1HBOCcP0DdxGNo4-nOZcF0WZIDoCtTdw5mZIOSt0veif…:2 

OPTIONS <code>http://myLocalWebServer.company.com/MyPage/Service.svc/GetDropDownData</code> No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin <code>'https://gate.company.com'</code> is therefore not allowed access. ScriptResource.axd?d=8mniuUQAKIvBIxCF_O9BRQpND31cf-SHqs1HBOCcP0DdxGNo4-nOZcF0WZIDoCtTdw5mZIOSt0veif…:2 

XMLHttpRequest cannot load <code>http://myLocalWebServer.company.com/MyPage/Service.svc/GetDropDownData</code>. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin <code>'https://gate.company.com'</code> is therefore not allowed access. Page.aspx?ObjectIdentity=f5c0c016-4828-4935-a7a9-73f3ba47a1ed:1 

Refused to get unsafe header "jsonerror" 

ho scoperto che utilizzando il crossDomainScriptAccessEnabled nella configurazione di legame webservice nel web.config e utilizzando una chiamata JSONP Ajax tramite jQuery, ha fatto il lavoro (vedi sopra per il codice) MA: Funzionava solo nell'area non autorizzata della pagina. Non appena l'utente si è autenticato sulla pagina di accesso, la chiamata non ha funzionato più. Mi ha detto Cross domain javascript callback is not supported in authenticated services.

Infine, ho trovato la soluzione. Il messaggio di errore veniva da crossDomainScriptAccessEnabled configurato nel web.config. Quando l'ho rimosso, la chiamata jsonp non ha funzionato più. Quindi quello che ho fatto è rimuovere il crossDomainScriptAccessEnabled e sostituire la chiamata jsonp con una normale chiamata json.

jQuery.ajax({ 
     type: "POST", 
     dataType: "json", 
     contentType: "application/json; charset=utf-8", 
     cache: true, 
     url: "Service.svc/GetDropDownData", 
     data: '{"dropId":"' + dropId + '"}', 
     error: function (xhr, textStatus, errorThrown) { 
      // Ignore in my case... 
     }, 
     success: function (data, textStatus, xhr) { 
      fillSubList(data.d); 
     } 
    }); 

function fillSubList(result) { 
    var theDropDown = jQuery("#<%= cboSelektion.ClientID %>"); 
    if (theDropDown.length > 0) { 
     //Clear the old entries 
     theDropDown.empty(); 

     //Add the empty one 
     if ("<%= cboSelektion.ShowEmptyRow %>".toLowerCase() == "true") { 
      theDropDown.append($('<option></option>')); 
     } 

     // Add the found items 
     for (var i = 0; i < result.length; i++) { 
      var text = result[i]; 
      theDropDown.append($('<option></option>').val(text).html(text)); 
     } 
    } 
} 
+4

rimuovere la proprietà 'crossDomainScriptAccessEnabled' nel bind di configurazione è stato risolto per me. – Seany84