Pergunta

Eu escrevi um aplicativo Silverlight 2 comunicando-se com um serviço WCF (BasicHttpBinding).O site que hospeda o conteúdo do Silverlight é protegido por um provedor de associação ASP.NET.Posso acessar o usuário atual usando HttpContext.Current.User.Identity.Name do meu serviço WCF e ativei o AspNetCompatibilityRequirementsMode.

Agora quero escrever um aplicativo do Windows usando exatamente o mesmo serviço da web.Para lidar com a autenticação, habilitei o Serviço de autenticação, e posso chamar "login" para autenticar meu usuário...Ok, tudo bem...Mas como diabos faço para definir esse cookie de autenticação em meu outro cliente de serviço?!

Ambos os serviços estão hospedados no mesmo domínio

  • MyDataService.svc <- aquele que lida com meus dados
  • AuthenticationService.svc <- aquele que o aplicativo do Windows deve chamar para autenticar.

Não quero criar um novo serviço para o cliente Windows ou usar outra ligação...

O Client Application Services é outra alternativa, mas todos os exemplos se limitam a mostrar como obter o usuário, funções e seu perfil...Mas, uma vez autenticados usando os serviços de aplicativos clientes, deverá haver uma maneira de anexar esse cookie de autenticação aos meus clientes de serviço ao ligar de volta para o mesmo servidor.

De acordo com a opinião de colegas, a solução é adicionar um ponto final wsHttpBinding, mas espero poder contornar isso...

Foi útil?

Solução

Finalmente encontrei uma maneira de fazer isso funcionar.Para autenticação estou usando o "Serviço de autenticação WCF".Ao autenticar o serviço tentará definir um cookie de autenticação.Preciso retirar esse cookie da resposta e adicioná-lo a qualquer outra solicitação feita a outros serviços da Web na mesma máquina.O código para fazer isso é assim:

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

Como você pode ver, tenho dois clientes de serviço, um para o serviço de autenticação e outro para o serviço que realmente irei usar.O primeiro bloco chamará o método Login e extrairá o cookie de autenticação da resposta.O segundo bloco adicionará o cabeçalho à solicitação antes de chamar o método de serviço "GetDives".

Não estou nada satisfeito com este código e acho que uma alternativa melhor seria usar "Referência da Web" em vez de "Referência de serviço" e usar a pilha .NET 2.0.

Outras dicas

Os serviços da Web, como os criados pelo WCF, geralmente são melhor usados ​​de maneira "sem estado", de modo que cada chamada para um serviço da Web começa novamente.Isso simplifica o código do servidor, pois não há necessidade de uma “sessão” que recupere o estado do cliente.Também simplifica o código do cliente, pois não há necessidade de armazenar tickets, cookies ou outras coisas que pressupõem algo sobre o estado do servidor.

A criação de dois serviços da maneira descrita introduz a monitoração de estado.O cliente é "autenticado" ou "não autenticado" e MyDataService.svc precisa descobrir qual.

Acontece que descobri que o WCF funciona bem quando o provedor de associação é usado para autenticar todo ligue para um serviço.Portanto, no exemplo dado, você desejaria adicionar os gubbins de autenticação do provedor de associação à configuração do serviço para MyDataService e não ter nenhum serviço de autenticação separado.

Para obter detalhes, consulte o artigo do MSDN aqui.

[O que é muito atraente nisso para mim, já que sou preguiçoso, é que isso é totalmente declarativo.Eu simplesmente espalhei as entradas de configuração corretas para meu MembershipProvider no app.config do aplicativo e!Bingo!todas as chamadas para todos os contratos do serviço são autenticadas.]

É justo notar que isso não será particularmente rápido.Se estiver usando o SQL Server para seu banco de dados de autenticação, você terá pelo menos uma, talvez duas chamadas de procedimento armazenado por chamada de serviço.Em muitos casos (especialmente para ligações HTTP) a sobrecarga da própria chamada de serviço será maior;caso contrário, considere implementar sua própria implementação de um provedor de associação que armazene em cache solicitações de autenticação.

Uma coisa que isso não give é a capacidade de fornecer um recurso de "login".Para isso, você pode fornecer um contrato de serviço (autenticado!) que não faz nada (além de gerar uma falha se a autenticação falhar) ou pode usar o serviço de provedor de associação conforme descrito no artigo original referenciado.

No cliente, modifique sua tag <binding> do serviço (dentro de <system.serviceModel>) para incluir:permitirCookies = "verdadeiro"

O aplicativo agora deve persistir o cookie e usá-lo.Você notará que IsLoggedIn agora retorna verdadeiro após o login – ele retorna falso se você não estiver permitindo cookies.

É possível ocultar grande parte do código extra por trás de um inspetor e comportamento de mensagens personalizados, para que você não precise se preocupar em mexer no OperationContextScope sozinho.

Vou tentar zombar de algo mais tarde e enviar para você.

--larsw

Você deveria dar uma olhada no CookieContainer objeto em System.Net.Este objeto permite que um cliente que não seja navegador mantenha os cookies.Foi isso que minha equipe usou na última vez que enfrentamos esse problema.

Aqui está um breve artigo sobre como proceder para usá-lo.Pode haver outros melhores por aí, mas isso deve ajudar você a começar.

Seguimos o caminho sem estado para nosso conjunto atual de serviços WCF e aplicativo Silverlight 2.É possível fazer com que o Silverlight 2 funcione com serviços vinculados à segurança TransportWithMessageCredential, embora seja necessário algum código de segurança personalizado no lado do Silverlight.O resultado é que qualquer aplicativo pode acessar os serviços simplesmente definindo o nome de usuário e a senha nos cabeçalhos das mensagens.Isso pode ser feito uma vez em uma implementação personalizada do IRequestChannel para que os desenvolvedores nunca precisem se preocupar em definir os valores por conta própria.Embora o WCF tenha uma maneira fácil para os desenvolvedores fazerem isso, acredito que seja serviceProxy.Security.Username e serviceProxy.Security.Password ou algo igualmente simples.

Escrevi isso há algum tempo, quando estava usando Client Application Services para autenticação em serviços da web.Ele usa um inspetor de mensagens para inserir o cabeçalho do cookie.Existe um arquivo word com documentação e um projeto de demonstração.Embora não seja exatamente o que você está fazendo, é bem próximo.Você pode baixá-lo em aqui.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top