2010-04-06 10 views
14

De acuerdo, he tenido poca suerte en encontrar documentación o tutoriales para mi caso específico.Cómo usar la cookie de autenticación desde WCF Authentication Service en una aplicación ASP.Net MVC

Tengo una aplicación web ASP.Net MVC que utilizará los servicios de WCF para todo, incluida la autenticación y las funciones (a través de proveedores de membresía en el backend de WCF).

No he tenido ningún problema setting up the authentication services pero no establece una cookie en la aplicación web. Los documentos para Login method of the service indican que es posible cablear el evento CreatingCookie, pero no tiene ningún efecto en el cliente (lo intenté por el lado del servicio también, de nuevo sin efecto). Así que descubrí cómo capture the cookie. Intenté configurar manualmente la cookie de autenticación en el cliente, pero hasta ahora no funciona; el descifrado falla debido al relleno, y el cliente no puede leer el valor de la cookie del que le proporcionó el servidor.

¿Alguien sabe cómo se supone que debe utilizar la cookie generada por el Servicio de Autenticación WCF? ¿Asumo que la sesión está administrada en el servidor WCF y solo compruebo IsLoggedIn() en el servicio en cada carga de página?

Gracias de antemano.

+0

leyendo más documentación en MSDN, supongo que mi escenario nunca fue intencionado por MS; si va a utilizar la membresía en un sitio web, ¿por qué alguna vez se autenticaría en un servicio en lugar de ingresar directamente a la base de datos, por lo que parece que un sitio web debe manejarse igual que cualquier otro cliente, lo que significa tomar manualmente las cookies de el encabezado y usándolos usted mismo (creando un principal, un ticket de autenticación, etc. y asignándolos a las partes apropiadas del sitio web para que pueda ser utilizado). investigará más y con suerte tendrá una respuesta para publicar ... – tap

Respuesta

11

Recientemente he estado tratando de implementar la misma funcionalidad que usted ha descrito. He logrado que funcione con el siguiente código:

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); 
      } 
     } 
    } 

Hace exactamente lo que ha descrito el comentario a su pregunta.

EDITAR

estoy publicando aquí la parte del lado del servidor de este código de referencia.

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, extrañé algunos trucos ... Déjame ver cómo funciona esto y me pondré en contacto contigo. No importa lo que realmente aprecio tu esfuerzo y te interese :) – tap

+0

esto se ve bien en la superficie, pero estoy viendo el mismo problema con el que me encontré: System.Security.Cryptography.CryptographicException: el relleno no es válido y no se puede eliminar. Mi suposición sobre este error es que, dado que se trata de 2 contextos diferentes (aplicación de cliente y aplicación de servicio), el descifrado/cifrado no es compatible? ¿Cómo se configuró para que funcione? De nuevo gracias por tomar un interés y el esfuerzo :) – tap

+0

Es la aplicación host Asp.net MVC y WCF se ejecuta en la misma máquina? Si no, tendrá que configurar ambos Web.config para usar la misma clave de máquina. Ver [http://msdn.microsoft.com/en-us/library/ms998288.aspx] En mi configuración, tanto MVC y WCF anfitrión Servicio se están ejecutando dentro de Studio Visual WebServer en mi máquina local. Entonces la clave de la máquina sería la misma. EDIT: También puede intentar añadir este código para que WCF alojar el archivo Global.cs para enviar explícitamente una cookie sólo para descartar una cosa más: http://msdn.microsoft.com/en-us/library/bb398778% 28v = VS.100% 29.aspx – Karl

2

Esto funciona para mí ... Comienzo de la configuración del comportamiento de autenticación del host (aquí se muestra a través de código, pero también se puede hacer en config):

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

Y entonces las clases de ayuda

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; } 
    } 
    } 

Y para cuando AspNetCompatibility se establece, entonces FormCookieServiceAuthorizationManager es un poco más sencillo:

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