2010-04-06 21 views
14

Ok, ho avuto poca fortuna a trovare documentazione o tutorial per il mio scenario specifico.Come utilizzare il cookie di autenticazione dal servizio di autenticazione WCF in un'applicazione ASP.Net MVC

Possiedo un'applicazione Web ASP.Net MVC che utilizzerà i servizi WCF per tutto ciò che include autenticazione e ruoli (tramite i provider di appartenenze sul back-end WCF).

Non ho avuto problemi setting up the authentication services ma non imposta un cookie nell'app web. I documenti per lo Login method of the service indicano che il cablaggio dell'evento CreatingCookie è possibile, ma non ha alcun effetto sul client (ho provato anche dal lato del servizio, ancora nessuna influenza). Così ho capito come fare a capture the cookie. Ho provato a impostare manualmente il cookie auth sul client, ma finora non funziona; la decrittografia non riesce a causa del riempimento e l'impostazione del valore del cookie da quello fornito dal server non è leggibile dal client.

Qualcuno sa come si suppone di utilizzare il cookie generato dal servizio di autenticazione WCF? Suppongo solo che la sessione sia interamente gestita sul server WCF e basta controllare IsLoggedIn() sul servizio ad ogni caricamento della pagina?

Grazie in anticipo.

+0

leggendo attraverso più documentazione su MSDN, immagino che il mio scenario non sia mai stato inteso dalla MS; se vuoi usare l'appartenenza a un sito web, perché dovresti mai autenticarti a un servizio invece di colpire direttamente il db, quindi sembra che un sito web debba essere gestito allo stesso modo di qualsiasi altro client, il che significa afferrare manualmente i cookie da l'intestazione e farne uso da soli (creando un principal, un ticket di autenticazione, ecc. e assegnandoli alle parti appropriate del sito Web in modo che possa essere utilizzato). indagherò di più e spero di avere una risposta per postare ... – tap

risposta

11

Recentemente ho cercato di implementare la stessa funzionalità che hai descritto. Sono riuscito a farlo funzionare con il seguente codice:

private readonly AuthenticationServiceClient service = new AuthenticationServiceClient(); 

    public void SignIn(string userName, string password, bool createPersistentCookie) 
    { 
     using (new OperationContextScope(service.InnerChannel)) 
     { 
      // login 
      service.Login(userName, password, String.Empty, createPersistentCookie); 

      // Get the response header 
      var responseMessageProperty = (HttpResponseMessageProperty) 
       OperationContext.Current.IncomingMessageProperties[HttpResponseMessageProperty.Name]; 

      string encryptedCookie = responseMessageProperty.Headers.Get("Set-Cookie"); 

      // parse header to cookie object 
      var cookieJar = new CookieContainer(); 
      cookieJar.SetCookies(new Uri("http://localhost:1062/"), encryptedCookie); 
      Cookie cookie = cookieJar.GetCookies(new Uri("http://localhost:1062/"))[0]; 

      FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value); 
      if (null != ticket) 
      { 
       //string[] roles = RoleManager.GetRolesFromString(ticket.UserData); 
       HttpContext.Current.User = new GenericPrincipal(new FormsIdentity(ticket), null); 
       FormsAuthentication.SetAuthCookie(HttpContext.Current.User.Identity.Name, createPersistentCookie); 
      } 
     } 
    } 

Fa esattamente quello che hai descritto il commento alla tua domanda.

EDIT

Vi metto qui la porzione lato server di questo codice di riferimento.

public class HttpResponseMessageInspector : BehaviorExtensionElement, IDispatchMessageInspector, IServiceBehavior 
{ 
    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) 
    { 

     HttpRequestMessageProperty httpRequest = request.Properties[HttpRequestMessageProperty.Name] 
     as HttpRequestMessageProperty; 

     if (httpRequest != null) 
     { 
      string cookie = httpRequest.Headers[HttpRequestHeader.Cookie]; 

      if (!string.IsNullOrEmpty(cookie)) 
      { 
       FormsAuthentication.Decrypt(cookie); 
       FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(cookie); 
       string[] roles = PrincipalHelper.GetUserRoles(authTicket); 
       var principal = new BreakpointPrincipal(new BreakpointIdentity(authTicket), roles); 

       HttpContext.Current.User = principal;     
      } 
      // can deny request here 
     } 

     return null; 
    } 
} 
+0

wow, mi sono perso alcuni trucchi .. fammi vedere come funziona e ti ricontatterò. Non importa cosa apprezzo davvero il tuo impegno e mi sto interessando :) – tap

+0

questo sembra buono in superficie, ma sto riscontrando lo stesso problema che ho riscontrato: System.Security.Cryptography.CryptographicException: Padding non è valido e non può essere rimosso. La mia ipotesi su questo errore è che dal momento che sono 2 diversi contesti (applicazione client e applicazione di servizio), decrypt/encrypt non è compatibile? Come hai impostato questo al lavoro? Ancora grazie per aver preso un interesse e lo sforzo :) – tap

+0

L'applicazione ASP.net MVC e WCF Host è in esecuzione sulla stessa macchina? In caso contrario, sarà necessario impostare entrambi Web.config per utilizzare la stessa chiave macchina. Vedere [http://msdn.microsoft.com/en-us/library/ms998288.aspx] Nella mia installazione, sia MVC che il servizio WCF sono in esecuzione all'interno di Visual Studio WebServer sul mio computer locale. Quindi la chiave della macchina sarebbe la stessa. EDIT: Puoi anche provare ad aggiungere questo codice al file Global.cs dell'host WCF per inviare esplicitamente un cookie solo per escludere più elementi: http://msdn.microsoft.com/en-us/library/bb398778% 28v = VS.100% 29.aspx – Karl

2

questo funziona per me ... Prima di impostazione del comportamento di autenticazione dell'ospite (qui mostrato tramite il codice, ma può anche essere fatto in config):

ServiceAuthorizationBehavior author = Description.Behaviors.Find<ServiceAuthorizationBehavior>(); 
author.ServiceAuthorizationManager = new FormCookieServiceAuthorizationManager(); 
author.PrincipalPermissionMode = PrincipalPermissionMode.Custom; 
author.ExternalAuthorizationPolicies = new List<IAuthorizationPolicy> { new CustomAuthorizationPolicy() }.AsReadOnly(); 

E poi le classi di supporto

internal class FormCookieServiceAuthorizationManager : ServiceAuthorizationManager 
    { 
    public override bool CheckAccess(OperationContext operationContext) 
    { 
     ParseFormsCookie(operationContext.RequestContext.RequestMessage); 
     return base.CheckAccess(operationContext); 
    } 
    private static void ParseFormsCookie(Message message) 
    { 
     HttpRequestMessageProperty httpRequest = message.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty; 
     if (httpRequest == null) return; 

     string cookie = httpRequest.Headers[HttpRequestHeader.Cookie]; 
     if (string.IsNullOrEmpty(cookie)) return; 

     string regexp = Regex.Escape(FormsAuthentication.FormsCookieName) + "=(?<val>[^;]+)"; 
     var myMatch = Regex.Match(cookie, regexp); 
     if (!myMatch.Success) return; 

     string cookieVal = myMatch.Groups["val"].ToString(); 
     FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(cookieVal); 
     Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(authTicket.Name), new string[0]); 
    } 
    } 
    internal class CustomAuthorizationPolicy : IAuthorizationPolicy 
    { 
    static readonly string _id = Guid.NewGuid().ToString(); 
    public string Id 
    { 
     get { return _id; } 
    } 

    public bool Evaluate(EvaluationContext evaluationContext, ref object state) 
    { 
     evaluationContext.Properties["Principal"] = Thread.CurrentPrincipal; 
     evaluationContext.Properties["Identities"] = new List<IIdentity> { Thread.CurrentPrincipal.Identity }; 
     return true; 
    } 

    public ClaimSet Issuer 
    { 
     get { return ClaimSet.System; } 
    } 
    } 

E per quando AspNetCompatibility è impostato, quindi FormCookieServiceAuthorizationManager è leggermente più semplice:

internal class FormCookieServiceAuthorizationManager : ServiceAuthorizationManager 
{ 
    public override bool CheckAccess(OperationContext operationContext) 
    { 
     Thread.CurrentPrincipal = HttpContext.Current.User; 
     return base.CheckAccess(operationContext); 
    } 
} 
Problemi correlati