none
Compartilhando contexto (Identity) entre serviço WCF e um site usando FormsAuthentication RRS feed

  • Pergunta

  • Saudações!!!

     

    Eu estou com um problema de integração com WCF. O cenário é o seguinte: Eu tenho um site, com autenticação Forms. Eu também tenho um serviço WCF (publicado no IIS) que é consumido pelo site. Ambos estão na mesma rede.

     

    Quando eu chamo o serviço WCF, eu gostaria que o serviço aproveitasse a sessão de segurança do site (FormsAuthentication) para autenticar a chamada do serviço WCF, e além de garantir a segurança, eu gostaria de usar o usuário (Identity) da sessão para logging.

     

    Além disso, eu preciso que esta passagem seja transparente para quem consome o serviço, ou seja, sem codificação adicional no cliente para ter esta funcionalidade, sendo apenas uma questão de configuração do serviço no client.

     

    Se isso não for possível de alguma maneira, eu vou ter que incluir o id do usuário na chamada do serviço.

     

    Eu agradeço desde já a ajuda de vocês!

     

     

    Abs,

    Thiago Varea

    segunda-feira, 22 de setembro de 2008 15:31

Respostas

  • Ola Thiago,

    Como você está entre aplicações, eu acredito que o mais correto seria você adicionar a referência para o serviço na sua aplicação Web e, através do proxy, autenticar o usuário, muito próximo ao exemplo que tem neste artigo: http://www.projetando.net/Sections/ViewArticle.aspx?ArticleID=67.

     

    De qualquer forma, você consegue recuperar o username do lado do serviço. Como disse anteriormente, ao utilizar o FormsAuthentication, ele cria um cookie para ser utilizado pelo sistema de autenticação, colocando dentro deste o nome do usuário autenticado pelo aplicação ASP.NET. Se você consegue garantir o mesmo machineKey (validationKey/decryptionKey) em ambos arquivos Web.config (ASP.NET e Serviço), então você pode recuperar o valor que está contido no cookie.


    Para exemplificar, podemos colocar as mesmas informações nos arquivos de configuração. Para detalhes de como criar/gerenciar isso, consulte este link: http://weblogs.pontonetpt.com/israelaece/posts/26416.aspx.


    Depois disso, dentro do seu método do serviço que está exposto via REST, devemos habilitar a compatibilidade com o ASP.NET e, utilizar a classe HttpContext para extrair as informações provenientes da requisição. O exemplo abaixo ilustra isso:

     

    Code Snippet

    using System;
    using System.Web;
    using System.Web.Security;
    using System.ServiceModel;
    using System.ServiceModel.Activation;

     

    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
    public class Service : IService
    {
        public string GetData()
        {
            HttpContext context = HttpContext.Current;
            string retorno = string.Empty;

            if (context != null)
            {
                if (context.Request != null)
                {
                    if (context.Request.Cookies != null)
                    {
                        HttpCookie cookie = context.Request.Cookies.Get(".ASPXAUTH");
                        string valorCriptografado = cookie.Value;

                        if (!string.IsNullOrEmpty(valorCriptografado))
                        {
                            retorno = FormsAuthentication.Decrypt(valorCriptografado).Name;
                        }
                    }
                }
            }

            return retorno;
        }
    }

     

     

    O valor .ASPXAUTH é o valor padrão do nome do cookie de autenticação do ASP.NET. Caso você mude isso, atente-se para colocar o mesmo nome do lado do serviço. Além disso, habilite a compartibilidade do ASP.NET na configuração do ServiceHost que, no caso de projetos do tipo WCF Service, estará no arquivo Web.Config:


    <system.serviceModel>
      <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
     
      <!-- outras configurações -->
    </system.serviceModel>

    terça-feira, 23 de setembro de 2008 23:49
    Moderador

Todas as Respostas

  • Ola Thiago,

    Já tentou dar uma olhada na ASP.NET Compatibility Mode: http://www.projetando.net/Sections/ViewArticle.aspx?ArticleID=66 ?

    Mais detalhes aqui: http://msdn.microsoft.com/en-us/library/aa702682.aspx

    segunda-feira, 22 de setembro de 2008 18:43
    Moderador
  • Israel,

     

    Eu já tentei isto sim. Na verdade, antes de criar este post, eu tentei algumas soluções que eu achei na internet dando as minhas googladas, porém nenhuma foi bem sucedida.

     

    Embora no site, o UserName seja igual ao definido pelo FormsAuthentication, quando eu chego no serviço, ele vira autenticação Windows, com o UserName do usuário utilizado pelo site e não o definido pelo FormsAuthentication. Eu tentei ativar impersonation e outras soluções, mas não funcionaram.

     

    Eu achei uns artigos que criavam um Behavior do serviço e utilizavam um DispatchMessageInspector para pegar o HttpContext.Current.User do site e jogar no Thread.CurrentPrincipal, mas também não funcionou. Eu não conheço direito esta solução usando Behavior e DispatchMessageInspector, mas eu fiz igual ao exemplo e não funcionou como eu queria.

     

    Então, depois de muito teste, eu desisti e resolvi recorrer a este fórum.

     

    Sei que a solução não deve ser simples, como ligar a compatibilidade ASP.NET, mas espero encontrá-la neste fórum.

     

    Continuo aguardando ajuda! Obrigado!

    segunda-feira, 22 de setembro de 2008 19:19
  • Ola Thiago,

    Eu consegue reproduzir aqui e consegue recuperar o Identity fornecido pelo ASP.NET dentro do serviço SVC. Basicamente o que eu fiz foi criar um serviço com um método apenas, implementá-lo a partir de REST para conseguir capturar o resultado do método no browser.


    Code Snippet

    [ServiceContract]
    public interface IService
    {
        [OperationContract]
        [WebGet(UriTemplate = "DoWork")]
        string DoWork();
    }


    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
    public class Service : IService
    {
        public string DoWork()
        {
            if (string.IsNullOrEmpty(HttpContext.Current.User.Name))
                return "null";
            else
                return HttpContext.Current.User.Identity.Name;
        }
    }

     

     


    E no ASP.NET autenticá-lo:


    if (Membership.ValidateUser("israelaece", "P@$$w0rd"))
        FormsAuthentication.RedirectFromLoginPage("israelaece", false);

    Desde modo, o serviço está retornando "israelaece". Era isso o que precisa?

    segunda-feira, 22 de setembro de 2008 21:35
    Moderador
  • Israel,

    Eu fiz exatamente como mostra o seu artigo sobre REST, porém, novamente, aconteceu o que eu disse no primeiro post, ele não considera o usuário autenticado na web, mas ele retorna o usuário do windows.

     

    Este usuário que você autenticou no FormsAuthentication é o mesmo que vc usa para logar na rede? Se sim, teste com um usuário diferente, como "israel" apenas e veja se ele retorna "israel" ou "israelaece" e me diga o que retornou, ok?

     

    Talvez esteja faltando algo no meu web.config. Você poderia enviar suas configurações de host e client?

     

    Agradeço sua disposição para me ajudar! Obrigado!

    terça-feira, 23 de setembro de 2008 14:32
  • Ola Thiago,

    Quando você utiliza FormsAuthentication, o usuário que é validado no Membership é armazenado na propriedade User do contexto HTTP. A autenticação está sendo utilizada é a Forms (baseada em cookies) e não tenho o impersonate habilitado.

    No exemplo que criei, não estou utilizando em nenhum momento o usuário do Windows, apenas o usuário que foi autenticado no site, utilizando a própria infraestrutura já fornecida pelo ASP.NET. No meu caso, tanto o ASPX quanto o arquivo SVC estão localizados dentro do mesmo diretório virtual, ou seja, não são duas aplicações (diretórios virtuais) diferentes.

    terça-feira, 23 de setembro de 2008 14:45
    Moderador
  • Israel,

    No meu caso, a aplicação web e o serviço (ISS) ficam em servidores separados. Este é meu ambiente. Eu consigo autenticar o usuário usando Membership/FormsAuthentication no site, mas o usuário do contexto no serviço fica "null" no serviço.

     

    Eu já tentei usar o enableCrossAppRedirects com o mesmo machineKey, mas não adiantou. O UserName do contexto continua null.

     

    É possível fazer esta sua solução funcionar no meu ambiente? Como?

    terça-feira, 23 de setembro de 2008 18:09
  • Ola Thiago,

    Como você está entre aplicações, eu acredito que o mais correto seria você adicionar a referência para o serviço na sua aplicação Web e, através do proxy, autenticar o usuário, muito próximo ao exemplo que tem neste artigo: http://www.projetando.net/Sections/ViewArticle.aspx?ArticleID=67.

     

    De qualquer forma, você consegue recuperar o username do lado do serviço. Como disse anteriormente, ao utilizar o FormsAuthentication, ele cria um cookie para ser utilizado pelo sistema de autenticação, colocando dentro deste o nome do usuário autenticado pelo aplicação ASP.NET. Se você consegue garantir o mesmo machineKey (validationKey/decryptionKey) em ambos arquivos Web.config (ASP.NET e Serviço), então você pode recuperar o valor que está contido no cookie.


    Para exemplificar, podemos colocar as mesmas informações nos arquivos de configuração. Para detalhes de como criar/gerenciar isso, consulte este link: http://weblogs.pontonetpt.com/israelaece/posts/26416.aspx.


    Depois disso, dentro do seu método do serviço que está exposto via REST, devemos habilitar a compatibilidade com o ASP.NET e, utilizar a classe HttpContext para extrair as informações provenientes da requisição. O exemplo abaixo ilustra isso:

     

    Code Snippet

    using System;
    using System.Web;
    using System.Web.Security;
    using System.ServiceModel;
    using System.ServiceModel.Activation;

     

    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
    public class Service : IService
    {
        public string GetData()
        {
            HttpContext context = HttpContext.Current;
            string retorno = string.Empty;

            if (context != null)
            {
                if (context.Request != null)
                {
                    if (context.Request.Cookies != null)
                    {
                        HttpCookie cookie = context.Request.Cookies.Get(".ASPXAUTH");
                        string valorCriptografado = cookie.Value;

                        if (!string.IsNullOrEmpty(valorCriptografado))
                        {
                            retorno = FormsAuthentication.Decrypt(valorCriptografado).Name;
                        }
                    }
                }
            }

            return retorno;
        }
    }

     

     

    O valor .ASPXAUTH é o valor padrão do nome do cookie de autenticação do ASP.NET. Caso você mude isso, atente-se para colocar o mesmo nome do lado do serviço. Além disso, habilite a compartibilidade do ASP.NET na configuração do ServiceHost que, no caso de projetos do tipo WCF Service, estará no arquivo Web.Config:


    <system.serviceModel>
      <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
     
      <!-- outras configurações -->
    </system.serviceModel>

    terça-feira, 23 de setembro de 2008 23:49
    Moderador