Domanda

Ho scritto un'applicazione Silverlight 2 che comunica con un servizio WCF (BasicHttpBinding).Il sito che ospita il contenuto Silverlight è protetto tramite un provider di appartenenze ASP.NET.Posso accedere all'utente corrente utilizzando HttpContext.Current.User.Identity.Name dal mio servizio WCF e ho attivato AspNetCompatibilityRequirementsMode.

Ora voglio scrivere un'applicazione Windows utilizzando esattamente lo stesso servizio web.Per gestire l'autenticazione ho abilitato il file Servizio di autenticazione, e posso chiamare "login" per autenticare il mio utente...Ok, tutto bene...Ma come diavolo faccio a ottenere il cookie di autenticazione impostato sull'altro mio client di servizio?!

Entrambi i servizi sono ospitati sullo stesso dominio

  • MyDataService.svc <- quello che si occupa dei miei dati
  • AuthenticationService.svc <- quello che l'app Windows deve chiamare per autenticarsi.

Non voglio creare un nuovo servizio per il client Windows o utilizzare un'altra associazione...

I servizi dell'applicazione client sono un'altra alternativa, ma tutti gli esempi sono limitati a mostrare come ottenere l'utente, i ruoli e il suo profilo...Ma una volta autenticati utilizzando i servizi dell'applicazione client, dovrebbe esserci un modo per collegare il cookie di autenticazione ai client del mio servizio quando si richiama allo stesso server.

Secondo il contributo dei colleghi, la soluzione è aggiungere un endpoint wsHttpBinding, ma spero di poter aggirare il problema...

È stato utile?

Soluzione

Finalmente ho trovato un modo per farlo funzionare.Per l'autenticazione sto utilizzando "Servizio di autenticazione WCF".Durante l'autenticazione il servizio proverà a impostare un cookie di autenticazione.Devo eliminare questo cookie dalla risposta e aggiungerlo a qualsiasi altra richiesta effettuata ad altri servizi Web sulla stessa macchina.Il codice per farlo è simile al seguente:

var authService = new AuthService.AuthenticationServiceClient();
var diveService = new DiveLogService.DiveLogServiceClient();

string cookieHeader = "";
using (OperationContextScope scope = new OperationContextScope(authService.InnerChannel))
{
    HttpRequestMessageProperty requestProperty = new HttpRequestMessageProperty();
    OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestProperty;
    bool isGood = authService.Login("jonas", "jonas", string.Empty, true);
    MessageProperties properties = OperationContext.Current.IncomingMessageProperties;
    HttpResponseMessageProperty responseProperty = (HttpResponseMessageProperty)properties[HttpResponseMessageProperty.Name];
    cookieHeader = responseProperty.Headers[HttpResponseHeader.SetCookie];                
}

using (OperationContextScope scope = new OperationContextScope(diveService.InnerChannel))
{
    HttpRequestMessageProperty httpRequest = new HttpRequestMessageProperty();
    OperationContext.Current.OutgoingMessageProperties.Add(HttpRequestMessageProperty.Name, httpRequest);
    httpRequest.Headers.Add(HttpRequestHeader.Cookie, cookieHeader);
    var res = diveService.GetDives();
}      

Come puoi vedere, ho due client di servizio, uno per il servizio di autenticazione e uno per il servizio che utilizzerò effettivamente.Il primo blocco chiamerà il metodo Login e estrarrà il cookie di autenticazione dalla risposta.Il secondo blocco aggiungerà l'intestazione alla richiesta prima di chiamare il metodo di servizio "GetDives".

Non sono affatto soddisfatto di questo codice e penso che un'alternativa migliore potrebbe essere quella di utilizzare "Riferimento Web" invece di "Riferimento al servizio" e utilizzare invece lo stack .NET 2.0.

Altri suggerimenti

I servizi Web, come quelli creati da WCF, sono spesso utilizzati al meglio in modalità "senza stato", quindi ogni chiamata a un servizio Web inizia da capo.Ciò semplifica il codice del server, poiché non è necessario avere una "sessione" che richiami lo stato del client.Semplifica inoltre il codice client poiché non è necessario conservare ticket, cookie o altri geegaw che presuppongono qualcosa sullo stato del server.

La creazione di due servizi nel modo descritto introduce lo stato.Il client è "autenticato" o "non autenticato" e MyDataService.svc deve capire quale.

In effetti, ho riscontrato che WCF funziona bene quando il provider di appartenenze viene utilizzato per l'autenticazione ogni chiamare ad un servizio.Pertanto, nell'esempio fornito, vorresti aggiungere i gubbin di autenticazione del provider di appartenenze alla configurazione del servizio per MyDataService e non avere affatto un servizio di autenticazione separato.

Per i dettagli, vedere l'articolo MSDN Qui.

[La cosa più interessante per me, dato che sono pigro, è che è del tutto dichiarativo.Spargi semplicemente le voci di configurazione corrette per il mio MembershipProvider in app.config per l'applicazione e!tombola!tutte le chiamate a ogni contratto del servizio vengono autenticate.]

È giusto notare che questo non sarà particolarmente rapido.Se utilizzi SQL Server per il database di autenticazione avrai almeno una, forse due chiamate di procedura memorizzata per chiamata di servizio.In molti casi (specialmente per i collegamenti HTTP) il sovraccarico della chiamata al servizio stesso sarà maggiore;in caso contrario, valuta la possibilità di implementare la tua implementazione di un provider di appartenenze che memorizzi nella cache le richieste di autenticazione.

Una cosa è questa no give è la capacità di fornire una funzionalità di "accesso".Per questo, puoi fornire un contratto di servizio (autenticato!) che non fa nulla (a parte sollevare un errore se l'autenticazione fallisce), oppure puoi utilizzare il servizio del fornitore di abbonamento come descritto nell'articolo di riferimento originale.

Sul client modifica il tag <binding> per il servizio (all'interno di <system.serviceModel>) per includere:consentireCookies="true"

L'app ora dovrebbe mantenere il cookie e utilizzarlo.Noterai che IsLoggedIn ora restituisce true dopo l'accesso: restituisce false se non consenti i cookie.

È possibile nascondere gran parte del codice extra dietro un controllo e un comportamento dei messaggi personalizzati in modo da non doverti occupare di armeggiare tu stesso con OperationContextScope.

Proverò a prendere in giro qualcosa più tardi e te lo invierò.

--larsw

Dovresti dare un'occhiata a Contenitore dei cookie oggetto in System.Net.Questo oggetto consente a un client non browser di trattenere i cookie.Questo è ciò che il mio team ha utilizzato l'ultima volta che abbiamo riscontrato questo problema.

Ecco un breve articolo su come utilizzarlo.Potrebbero essercene di migliori là fuori, ma questo dovrebbe aiutarti a iniziare.

Abbiamo seguito il percorso stateless per il nostro attuale set di servizi WCF e l'applicazione Silverlight 2.È possibile far funzionare Silverlight 2 con i servizi legati alla sicurezza TransportWithMessageCredential, anche se richiede un codice di sicurezza personalizzato sul lato Silverlight.Il risultato è che qualsiasi applicazione può accedere ai servizi semplicemente impostando Username e Password nelle intestazioni dei messaggi.Questa operazione può essere eseguita una sola volta in un'implementazione IRequestChannel personalizzata in modo che gli sviluppatori non debbano mai preoccuparsi di impostare i valori da soli.Sebbene WCF abbia un modo semplice per gli sviluppatori di farlo, credo che sia serviceProxy.Security.Username e serviceProxy.Security.Password o qualcosa di altrettanto semplice.

L'ho scritto qualche tempo fa quando utilizzavo i servizi delle applicazioni client per l'autenticazione con i servizi Web.Utilizza un controllo messaggi per inserire l'intestazione del cookie.C'è un file word con documentazione e un progetto demo.Anche se non è esattamente quello che stai facendo, è abbastanza vicino.Puoi scaricarlo da Qui.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top