Utilizando o Url Routing no WCF

by Israel Aece 4. August 2010 12:19

Quando recorremos à uma das templates para a construção de serviços WCF (WCF Service Application ou WCF Service), ambas utilizam arquivos *.svc que representam o endpoint do serviço. Dentro deste arquivo temos apenas uma diretiva chamada @ServiceHost, que instrui o ASP.NET a como compilar o referido serviço.

Abaixo podemos visualizar um exemplo deste arquivo, que em seu atributo Service, permite apontar o tipo do serviço. Já no atributo CodeBehind, corresponde ao arquivo físico que possui a classe que representa o serviço e, finalmente, temos a atributo Factory, que é omitido por padrão, mas que tem a finalidade de criar instâncias da classe que gerencia o serviço (ServiceHost ou alguma de suas derivadas).

<%@ ServiceHost
    Language="C#"
    Debug="true"
    Service="ServicoDeClientes"
    Factory="System.ServiceModel.Activation.WebServiceHostFactory"
    CodeBehind="~/App_Code/ServicoDeClientes.cs" %>

No WCF 4.0, a Microsoft incluiu a possibilidade de criarmos serviços WCF sem a necessidade da presença de um arquivo *.svc. Isso eliminará completamente a necessidade deste arquivo. Mas para poder excluir o arquivo, tudo o que precisamos fazer é elencar os serviços no arquivo Web.config, utilizando o sub-elemento serviceActivations, exposto pelo elemento serviceHostingEnvironment:

<system.serviceModel>
  <serviceHostingEnvironment>
    <serviceActivations>
      <add
        relativeAddress="~/ServicoDeClientes.svc"
        service="Servicos.ServicoDeClientes"
        factory="System.ServiceModel.Activation.WebServiceHostFactory"/>
    </serviceActivations>
  </serviceHostingEnvironment>
</system.serviceModel>

Apesar de ainda precisar especifcar o arquivo no relativeAddress, ele não existe fisicamente. Note também que através do atributo service definimos o nome da classe que corresponde ao serviço e, finalmente, como já era de se esperar, podemos utilizar uma factory específica, que já configura o host com tudo o que é necessário para prepará-lo para receber e processar as requisições para uma determina situação. Com isso temos uma aplicação mais simples, onde tudo o que precisamos desenvolver é o contrato (interface) e o serviço (classe), assim como já fazemos com qualquer outro tipo de serviço WCF.

O maior problema desta técnica é a necessidade de ainda precisar informar a extensão do arquivo quando for efetuar uma requisição, e isso não é legal em um modelo REST. Lembre-se de que a funcionalidade que vimos acima, apenas elimina a necessidade do arquivo físico. Remover a extensão *.svc do atributo relativeAddress, não resolverá, pois vamos nos deparar com uma exceção do tipo ConfigurationErrorsException, dizendo que o valor informado no atributo relativeAddress não possui uma extensão válida.

Além do Url Rewrite Module (que já discuti no final deste artigo), temos agora a possibilidade de integrar o sistema de roteamento de URL do ASP.NET aos serviços WCF. Também na versão 4.0 do WCF, a Microsoft incluiu no assembly System.ServiceModel.Activation uma classe chamada ServiceRoute, que permite especificarmos exatamente os mesmos parâmetros que vimos acima e, automaticamente, será processado pelo handler correspondente (ServiceRouteHandler). Com isso, no evento Application_Start do arquivo Global.asax, devemos registrar mais uma rota, essa específica para o nosso serviço:

protected void Application_Start(object sender, EventArgs e)
{
    RouteTable.Routes.Add(
        new ServiceRoute(
            "ServicoDeClientes",
            new WebServiceHostFactory(), 
            typeof(Servicos.ServicoDeClientes)));
}

A string que aparece sendo informada no construtor da classe ServiceRoute, define o nome do recurso para qual estaremos efetuando o roteamento. Os outros dois parâmetros refletem as mesmas configurações que vimos acima. Mas ainda é precisa se atentar a um detalhe: se você estiver utilizando um projeto que não vem com o módulo de roteamento de URL configurado, você terá que fazer isso manualmente, assim como eu mostro abaixo:

<system.web>
  <httpModules>
    <add
      name="UrlRoutingModule"
      type="System.Web.Routing.UrlRoutingModule, System.Web" />
  </httpModules>
</system.web>

O único detalhe aqui é a necessidade de habilitar o modelo de compatibilidade com o ASP.NET, caso contrário, esta funcionalidade não poderá ser utilizada. Para habilitar isso no contrato, precisamos recorrer ao atributo AspNetCompatibilityRequirementsAttribute, definindo-o como Allowed (para permitir que ele seja exposto por outros tipos de hosts), e depois devemos habilitar a compatibilidade no arquivo de configuração, através do elemento serviceHostingEnvironment, definindo o atributo aspNetCompatibilityEnabled como True. Abaixo é exibido as duas configurações:

namespace Servicos
{
    [AspNetCompatibilityRequirements(
        RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class ServicoDeClientes : IServico
    {
        //...
    }
}

<system.serviceModel>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>

Depois destas configurações, já podemos acessar o serviço diretamente sem informar a extensão *.svc. É importante dizer que isso só é necessário quando você hospeda o seu serviço no IIS, já que as requisições são sempre realizadas para um arquivo físico. Quando você cria um serviço REST e deseja expor através de algum outro host, nada disso será necessário.

Tags: ,

WCF

Influencia do maxRequestLength no WCF

by Israel Aece 9. July 2010 10:17

Como eu comentei neste outro artigo, podemos utilizar no WCF um recurso chamado streaming, que é uma alternativa interessante quando precisamos enviar grandes quantidades de informações, que nestes cenários, o mais comum é o envio ou o recebimento de arquivos. Para que tudo isso funcione, precisamos nos atentar a efetuar algumas configurações no WCF com relação as cotas e timeouts, que impõem limites durante o tráfego das informações.

Em princípio, esses são os únicos cuidados que devemos ter. Poderemos começar a ter outros problemas, se esse serviço for hospedado no IIS. Para que fosse possível hospedar um serviço WCF no IIS, a Microsoft utilizou a estrutura do pipeline do ASP.NET para receber e desviar a requisição para serviços WCF para o seu respectivo handler. Antes de qualquer análise das cotas que configuramos para o serviço, há um detalhe importante que devemos nos preocupar. Trata-se da propriedade MaxRequestLength da classe HttpRuntime, que determina a quantidade máxima (em KBytes) de conteúdo que uma requisição do ASP.NET poderá receber, onde o seu valor padrão é 4 MB. Essa propriedade influencia quando queremos efetuar o upload de um arquivo grande através de uma aplicação ASP.NET.

Como a requisição chega para o IIS, que por sua vez delega o processamento para a estrutura do ASP.NET, o mesmo valida o tamanho deste conteúdo, e se for maior do que o valor estipulado nesta propriedade, você receberá uma exceção do tipo HttpException, com a seguinte mensagem: Maximum request length exceeded. É algo até complicado de se diagnosticar, já que essa exceção não é propagada para o cliente que consome o serviço, e o tracing do WCF neste caso, ajudará a desvendar este problema. De qualquer forma, quando for hospedar um serviço WCF no IIS, é importante que você sincronize as cotas com o atributo maxRequestLength exposto pelo elemento httpRuntime, assim como podemos notar no exemplo abaixo, que está configurado para receber um arquivo de até 100 MB:

<?xml version="1.0"?>
<configuration>
  <system.web>
    <httpRuntime maxRequestLength="102400"/>
  </system.web>
  <system.serviceModel>
    <services>
      <service name="Service">
        <endpoint address=""
                  binding="basicHttpBinding"
                  contract="IService"
                  bindingConfiguration="config" />
      </service>
    </services>
    <bindings>
      <basicHttpBinding>
        <binding name="config"
                 messageEncoding="Mtom"
                 transferMode="Streamed"
                 maxBufferSize="104857600"
                 maxBufferPoolSize="104857600"
                 maxReceivedMessageSize="104857600">
        </binding>
      </basicHttpBinding>
    </bindings>
</configuration>

Tags: , ,

WCF

Autenticação com WCF e jQuery

by Israel Aece 28. May 2010 21:48

Recentemente escrevi sobre o consumo de serviços WCF a partir do jQuery. Naquele artigo foi comentado como construir serviços WCF para ser acessados através do ambiente REST, e além disso, vimos também como proceder para o consumo deste serviço utilizando o jQuery como cliente.

Mas um detalhe que não foi abordado no artigo foi a questão da segurança, ou melhor, do processo de autenticação do cliente que consome o serviço. Por exemplo, imagine que temos um serviço que exige que o usuário se identifique, para que assim possamos determinar se ele terá ou não acesso ao serviço. Apesar do WCF fornecer várias alternativas para isso, quando estamos em um ambiente REST, alguns cuidados especiais são necessários, quais serão abordados no decorrer deste artigo.

O primeiro detalhe para nos atentarmos é como vamos configurar o serviço para que o mesmo possa exigir as credenciais. Para expor um serviço para ser consumido através do ambiente REST, utilizamos um binding exclusivo chamado de WebHttpBinding. Em sua configuração padrão, não há nenhum tipo de autenticação configurada, mas como qualquer binding, podemos recorrer à opção security para isso.

Temos apenas três opções relacionadas à segurança para este binding: None, Transport e TransportCredentialOnly. A primeira opção desabilita qualquer tipo de proteção e autenticação no respectivo serviço. Já a segunda opção, Transport, determina que será o protocolo que deverá garantir a segurança da mensagem (HTTPS) e, finalmente, o TransportCredentialOnly, que não fornecerá integridade e confidencialidade na mensagem que está sendo trafegada, e utilizará apenas o protocolo HTTP exclusivamente para autenticação do usuário.

Depois desta configuração que determina como a comunicação deverá ser protegida, temos uma espécie de "sub-configuração" dela que precisamos nos atentar. Esta sub-configuração determina como as credenciais serão passadas para o serviço. Entre as várias formas, temos o HTTP Basic, que faz com que as credenciais viagem do cliente para o serviço sem qualquer espécie de criptografia. Esse modelo, em sua configuração padrão, tem uma forte afinidade com o Windows/Active Directory, ou seja, para utilizar esse recurso, as credenciais informadas do lado do cliente deverão refletir uma conta válida no Windows/Active Directory, algo que não vamos nos preocupar neste momento. A configuração do serviço WCF deverá ficar da seguinte forma:

<system.serviceModel>
  <services>
    <service
      name="RESTComBasicAuthentication.Services.ServicoDeUsuarios"
      behaviorConfiguration="config">
      <endpoint
        address=""
        binding="webHttpBinding"
        contract="RESTComBasicAuthentication.Services.IUsuarios"
        behaviorConfiguration="edpConfig"
        bindingConfiguration="bc" />
    </service>
  </services>
  <bindings>
    <webHttpBinding>
      <binding name="bc">
        <security mode="TransportCredentialOnly">
          <transport clientCredentialType="Basic" />
        </security>
      </binding>
    </webHttpBinding>
  </bindings>
  <!-- Outras Configurações -->
</system.serviceModel>

Quando utilizamos o modelo Basic, ao tentar acessar um recurso protegido, o próprio browser é capaz de identificar e abrir uma janela com os campos para informarmos o username e password, só que não podemos nos esquecer de que esse serviço será consumido por algum cliente, e que o foco aqui é o uso do jQuery, e com isso, de alguma forma precisamos enviar o username e password do cliente para o serviço. Como já sabemos, o jQuery oferece uma função chamada $.ajax, que nos permite configurar e efetuar a chamada para um determinado serviço ou algum outro método. Internamente, este método recorre a um objeto popularmente conhecido, que é o XMLHttpRequest. Este objeto é o responsável por gerenciar toda a comunicação entre o cliente e o serviço.

De acordo com a especificação deste objeto, há um método chamado Open, que entre os parâmetros convencionais, como endereço até o serviço, o tipo da requisição (Json ou Xml), há também como informar mais duas informações relacionadas ao processo de autenticação: username e password. Através dos parâmetros username e password, definimos duas strings que representam cada uma dessas informações. Como o jQuery abstrai o acesso a este objeto, a função $.ajax também nos permite informar o usuário e senha, como já mencionado acima. Sabendo da existência destes parâmetros, podemos configurar a chamada para o serviço através do jQuery da seguinte forma:

function RecuperarUsuario() {
    $.ajax(
    {
        type: "POST",
        username: "UsuarioQueExisteNoWindows",
        password: "123456",
        url: "http://localhost/Services/ServicoDeUsuarios.svc/RecuperarUsuario",
        contentType: "application/json",
        data: '{ "nome": "Israel Aece", "email": "ia@israelaece.com" }',
        processData: true,
        success:
            function (resultado) {
                alert(resultado.RecuperarUsuarioResult.Nome);
                alert(resultado.RecuperarUsuarioResult.Email);
            },
    });
}

Se você monitorar a requisição para este serviço com alguma ferramenta, como é o caso do Fiddler, você verá que o username e password não serão enviados até que realmente seja necessário. O modelo de autenticação Basic é baseado no processo conhecido como "Desafio HTTP 401", que funciona da seguinte forma:

  • O cliente solicita um recurso (página, serviço, etc.) que está protegido.
  • Ao detectar que o cliente não está autenticado, o servidor exige que ele se autentique e informe as credenciais a partir do modelo Basic. Isso é informado a partir de um header chamado WWW-Authenticate: Basic.
  • Neste momento, o servidor retorna uma resposta com o código 401 (Access Denied), que instrui o cliente (browser) a solicitar as credenciais de acesso.
  • Uma vez informado, o browser recria a mesma requisição, mas agora envia nos headers da mesma o usernamepassword codificados em Base64, sem qualquer espécie de criptografia. Na resposta, o header enviado é o Authorization: Basic [Username+Password Codificado].
  • Quando este header acima estiver presente, o servidor (IIS) é capaz de validá-lo no Windows/Active Directory, e se for um usuário válido, permitirá o acesso, caso contrário retornará a mesma resposta com código 401, até que ele digite uma credencial válida.

Apesar das credenciais estarem em hard-code no exemplo acima, nada impede de criarmos uma tela onde o cliente pode digitar um usuário e senha, mas lembrando que este usuário deverá exisitir no Windows/Active Directory.

Customização

O principal ponto negativo da solução acima é a necessidade de termos os usuários cadastrados no Windows, algo que pode ser inviável em um ambiente que não é controlado, como é o caso da internet. Algo muito comum em aplicações deste tipo, é a validação do usuário em uma base de dados, ou até mesmo, utilizando a estrutura do Membership do ASP.NET. Para atingirmos esse objetivo, precisamos entender um pouco mais sobre o processo de autenticação e também alguns pontos de estensibilidade.

Quando configuramos o modelo Basic, o IIS é o responsável por coordenar todo o processo de acesso ao recurso, e se o usuário não estiver autenticado, o próprio IIS devolve a mensagem com o código 401 e o header específico para o mesmo (1), para que o browser proceda com a solicitação das credenciais (2), e depois de informadas, refaz a requisição embutindo o header com as credencias codificadas (3). A imagem abaixo ilustra esse processo:



No exemplo que vimos acima, o único recurso protegido pelo modelo Basic é apenas o arquivo ServicoDeUsuarios.svc. Agora, se desejamos customizar, temos que trazer todo o controle do processo de autenticação para a aplicação, ou seja, não iremos mais querer que o IIS coordene o processo, que como sabemos, ele recorre ao Windows para validar os usuários. Sendo assim, precisamos permitir o acesso anônimo ao arquivo *.svc.

Há algum tempo eu comentei sobre a possibilidade de efetuar a customização da autenticação do WCF através de usuário e senha, utilizando a classe UserNamePasswordValidator para essa validação. O problema é que o binding WebHttpBinding não suporta a customização deste validador, o que nos obrigaria à descer o nível até o pipeline do ASP.NET para implementar algo específico para o WCF.

Ao invés disso, podemos recorrer aos interceptadores (Interceptors). Os interceptadores não estão nativamente dentro do WCF, mas estão disponíveis ao instalar o WCF-REST Starter Kit. Este kit nada mais é que um conjunto de funcionalidades que podemos incorporar aos nossos projetos WCF baseados em REST, tornando algumas tarefas comuns em algo mais simples de se resolver/implementar, como vai ser o caso aqui.

Ao instalar este kit, teremos à nossa disposição alguns novos assemblies, e entre eles um chamado Microsoft.ServiceModel.Web.dll. Uma das classes fornecidas por ele é a classe abstrata RequestInterceptor. Essa classe fornece um método abstrato chamado ProcessRequest, que permite interceptar a requisição, antes mesmo dela começar a ser executada, fornecendo um parâmetro do tipo RequestContext, que representa o contexto da requisição atual, qual será utilizado para a interação com o cliente que está tentando acessar o recurso.

Com esta possibilidade, podemos criar um interceptador que extrai o header da requisição HTTP que representa a credencial informada pelo usuário (WWW-Authenticate), e a valida em algum repositório de sua escolha, como uma base de dados. A partir daqui é necessário conhecermos como funciona o processo do modelo HTTP Basic, para conseguirmos dialogar com o cliente, para que assim ele consiga coordenar o processo de autenticação do usuário.

Como havia dito acima, o username e password não são enviados até que sejam efetivamente exigidos. Nesta customização, ao identificarmos que o header não está presente na requisição, precisamos configurar a resposta para o cliente com o código 401, que representa acesso não autorizado, e informar na resposta o mesmo header, para continuar obrigando o usuário a informar o username e password.

Para saber se o cliente informou as credenciais, precisamos detectar a presença do header chamado Authorization. Se existir, então precisamos decodificá-lo, utilizando o método FromBase64String da classe Convert, que dado uma string, retorna um array de bytes representando as credenciais separadas por um ":". Depois disso, tudo o que precisamos fazer é separá-los, para que assim podermos efetuar a validação em algum repositório. O código abaixo ilustra esse processo:

string credentials = mp.Headers["Authorization"];

if (!string.IsNullOrWhiteSpace(credentials))
{
    string decodedCredentials =
        Encoding.Default.GetString(Convert.FromBase64String(credentials.Substring(6)));

    int separator = decodedCredentials.IndexOf(':');
    username = decodedCredentials.Substring(0, separator);
    password = decodedCredentials.Substring(separator + 1);
}

Caso o header não exista ou o usuário não existir na base de dados, então podemos criar o header para que o cliente seja - novamente - obrigado a informar as credenciais. Para isso, utilizamos as propriedades da mensagem, e configuramos a mensagem de retorno. A mensagem é representada no WCF pela classe Message. O código abaixo ilustra a criação, configuração e resposta ao cliente com esta mensagem recém criada:

private void GenerateAccessDeniedMessage(ref RequestContext requestContext)
{
    Message reply = Message.CreateMessage(MessageVersion.None, null, null
        new DataContractJsonSerializer(typeof(object)));

    HttpResponseMessageProperty hrp = new HttpResponseMessageProperty();
    hrp.StatusCode = HttpStatusCode.Unauthorized;
    hrp.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", this.Realm));
    reply.Properties[HttpResponseMessageProperty.Name] = hrp;

    requestContext.Reply(reply);
    requestContext = null;
}

Se o usuário for válido, então é necessário inicializar o contexto de segurança do WCF, que é representado pela classe ServiceSecurityContext, que utiliza uma instância da classe GenericPrincipal para identificar o usuário logado.

Finalmente, tudo isso não funciona por si só. Ainda precisamos acoplar a instância desta classe à execução. Para isso, devemos recorrer a customização da classe que cria o host, que é responsável por gerenciar a execução da classe que representa o serviço. A classe ServiceHostFactory fornece um método chamado CreateServiceHost, que como o próprio nome diz, permite retornar qualquer classe que herde direta ou indiretamente da classe ServiceHost. Dentro do assembly do WCF-Rest Starter Kit, temos uma versão específica de host, chamada de WebServiceHost2. A grande diferença deste host, é que ele expõe uma propriedade chamada Interceptors, que permite adicionarmos qualquer interceptador, desde que ele herde da classe RequestInterceptor, como já vimos acima. Depois que a factory customizada foi criada, devemos alterar o arquivo *.svc para que ela seja utilizada:

<%@ ServiceHost
    Language="C#"
    Debug="true"
    Factory="RESTComBasicAuthentication.CustomRestHostFactory"
    Service="RESTComBasicAuthentication.Services.ServicoDeUsuarios"
    CodeBehind="ServicoDeUsuarios.svc.cs" %>

Uma vez que acoplamos este interceptador, ele é quem gerenciará o processo de autenticação, ou seja, o cliente irá dialogar com este interceptador, mas para que isso funcione corretamente, é necessário que a autenticação Basic esteja desabilitada no IIS. Através da imagem abaixo, podemos reparar este novo comportamento:

Conclusão: Apesar de não temos nativamente no WCF uma forma de customizar a autenticação em serviços REST, podemos recorrer ao WCF-REST Starter Kit para conseguir efetuar essa customização, utilizando a infraestrutura oferecida pelo modelo HTTP Basic para enviar as credenciais. Apesar de tudo funcionar como deveria, é importante dizer que os dados continuam sendo trafegados sem qualquer proteção, e para evitar que alguém visualize o conteúdo, é importante que toda a comunicação seja protegida pelo HTTPS.

RESTComBasicAuthentication.zip (328.00 kb)

Tags: , , ,

CSD | WCF

AppFabric para desenvolvedores WCF

by Israel Aece 6. May 2010 13:53

Temos dentro do sistema operacional uma ferramenta administrativa conhecida como Component Services. É através desta ferramenta que controlamos os componentes que utilizam a tecnologia COM+, permitindo visualizar aqueles componentes que estão hospedados em um determinado servidor, configurar suas características, entre várias outras funcionalidades.

Em muitas situações, o Component Services é encarado como sendo o servidor de aplicações, pois é nele que estarão todos os componentes que são utilizados pela companhia. Utilizamos o Component Services para centralizar tais componentes, fazendo uso de suas funcionalidades, tais como: suporte à transações, pool de objetos, sincronização, etc. As aplicações clientes, sejam elas Windows ou Web, recorrem a estes componentes hospedados neste servidor para executar as tarefas.

Com o surgimento do WCF, o COM+ está perdendo espaço, e a partir de agora, quando falamos em sistemas distribuídos, utilizamos o WCF como tecnologia para suportar isso. Uma das principais características do WCF, é a independência de hosting, ou seja, podemos utilizar qualquer tipo de aplicação para hospedar um serviço WCF.

No ambiente do WCF, um dos hosts que mais se popularizou foi o IIS, que até então, possibilitava apenas a publicação do serviço em cima do protocolo HTTP/HTTPS. A partir da versão 7.0 do IIS, ele já traz suporte à todos os protocolos, incluindo o TCP (WAS). Ao incorporar essa funcionalidade, isso o tornou muito mais do que um simples servidor para sites da web, dando a ele características de um servidor de aplicação, onde podemos hospedar componentes, que através do WCF, vamos publicar para os consumidores, estejam eles dentro do fora dos limites da empresa.

O IIS ganhou todas essas capacidades, mas as ferramentas que ajudam a monitorar esses serviços não evoluíram na mesma proporção. Por mais que eu consiga hospedar meus componentes no IIS, fazendo uso dos diversos protocolos, não há como, por exemplo, saber quais são os serviços WCF que eu tenho hospedado naquele servidor; quais são os endpoints publicados para um determinado serviço, entre várias outras informações úteis tanto para os desenvolvedores quanto para aqueles que administram o servidor.

Pensando nisso, a Microsoft criou uma ferramenta chamada de Windows Server AppFabric. Quando instalada, essa ferramenta adiciona ao sistema operacional, mais precisamente dentro do IIS, um conjunto de funcionalidades que permitem diagnosticar e gerenciar os serviços que rodam dentro de um determinado servidor. Além disso, o AppFabric ainda fornece recursos adicionais, como uma estrutura para caching distribuído, que pode ser utilizado para qualquer tipo de aplicação.

Entre os vários recursos oferecidos pelo AppFabric, temos a possibilidade de gerenciar e monitorar serviços WCF. Entre as funcionalidades exclusivas ao WCF, temos a capacidade de visualizar quais são os serviços que rodam naquele servidor, quais são os endpoints que cada serviço publica (incluindo aqueles de metadados). As imagens abaixo ilustram essas funcionalidades:

Ainda há uma opção chamada de Dashboard, que exibe uma visão sintética de quantas chamadas para serviços WCF ocorreram, quantas falharam, as exceções que foram disparadas, etc. Como trata-se de uma visão agrupada, essa tela possui vários links, que ao clicar, levará para uma segunda tela, com os filtros relacionados àquela opção clicada, fornecendo a visão analítica, com informações mais completas a respeito daquele serviço. A imagem abaixo ilustra este dashboard:

Ao instalar o AppFabric, um assistente é inicializado para configurá-lo. Basicamente, ele solicitará um local para armazenar as informações geradas por ele, para que mais tarde, possamos visualizá-las. Essas configurações exigem que você defina, através de uma espécie de provider, um banco de de dados SQL Server. É importante que você crie um banco exclusivo para isso, já que este assistente adicionará vários objetos dentro dele (tabelas, views, jobs, etc.), que são de uso exclusivo, e misturar com a base de dados da aplicação não é uma boa prática.

Além dessas informações macros, ainda temos um detalhamento delas. Quando utilizamos o tracing, uma série de informações são geradas, catalogando todos os estágios por onde a requisição está passando. No AppFabric temos uma seção chamada de Tracked Events, que nos permite visualizar justamente esse tipo de informação. Se analisarmos a imagem abaixo, podemos perceber os diversos eventos (estágios) do processamento, que ocorreram do lado do servidor, e também é possível visualizar a operação que foi efetivamente disparada.

Algumas poucas configurações do serviço estão disponíveis a partir do AppFabric, como é o caso de algumas cotas e throttling, mas como sabemos, as configurações do WCF são extensas e complexas, e neste caso, o uso da ferramenta WCF Service Configuration Editor performa melhor.

Para finalizar, um outro recurso bastante interessante, que também é fornecido com o AppFabric, é a possibilidade de exportar todas as configurações de um servidor, incluindo os serviços que nele rodam. Esse recurso utiliza em seus bastidores, uma ferramenta conhecida como Web Deployment Tool, e que permite configurar um outro servidor com as mesmas características deste servidor de origem, sem a necessidade de lembrar cada uma das configurações que você já realizou anteriormente.

Conclusão: O AppFabric não visa apenas serviços WCF. Como o Windows Workflow (WF) tem uma forte integração com o WCF, é possível gerenciarmos esses serviços a partir do AppFabric, com recursos para interagir com workflows de longa duração. Além disso, ainda há recursos extras que vão além do escopo definido para este artigo, mas que não são menos importantes. Depois de ter criado toda a infraestrutura necessária para suportar serviços WCF dentro do IIS, a Microsoft tem destinado grande parte de seus esforços no desenvolvimento desse tipo de ferramenta, que ajudam a monitorar e diagnosticar problemas, algo que muitas vezes são realizadas por outras pessoas, e que não são necessariamente desenvolvedores.

Tags: , ,

WCF

multipleSiteBindingsEnabled

by Israel Aece 4. May 2010 22:35

Há algum tempo eu comentei aqui sobre uma dificuldade que existe quando hospedamos serviços WCF no IIS. Neste mesmo post, eu mostrei a solução que foi incorporada na versão 3.5 do WCF.

Para facilitar isso, a partir da versão 4.0 do WCF temos uma opção chamada multipleSiteBindingsEnabled, disponível através do elemento serviceHostingEnvironment, que quando definido como True, permite que o serviço fique disponível a partir dos múltiplos endereços de acesso configurados no IIS. Abaixo temos o arquivo de configuração com esta opção habilitada:

<system.serviceModel>
    <serviceHostingEnvironment multipleSiteBindingsEnabled=”true”/>
</system.serviceModel>

Tags: , ,

WCF

Compressão em Serviços WCF

by Israel Aece 18. March 2010 00:16

Em certas situações, quando um cliente executa uma operação de um serviço, o resultado pode ser uma grande massa de dados. Essa massa de dados pode ser qualquer coisa, desde os bytes de um arquivo até mesmo objetos que foram extraídos de um banco de dados.

Como todos sabem, o problema disso é a quantidade de informações que irá trafegar na rede, podendo causar uma grande lentidão, caso isso seja uma operação comum. A principal solução que geralmente utilizamos quando queremos diminuir o tráfego, é a compactação das informações. Isso fará com que a quantidade total de dados seja bem menor, aliviando consideravelmente a quantidade de informações que são trafegadas. Obviamente que a compactação tem um overhead quando você compacta ou descompacta, e isso deve ser levado em consideração para decidir se deve ou não optar por ela, mas na maioria das vezes, acaba compensando.

Mas infelizmente o WCF não traz a possibilidade de compactar e/ou descompactar as mensagens que são enviadas. Sendo assim, para conseguirmos efetuar a compactação, temos que recorrer aos pontos de estensibilidade de WCF, para conseguir interceptar o envio e recebimento das mensagens, para assim conseguir diminuir o conteúdo a ser trafegado. A Microsoft se preocupou em deixar a disposição de todos, um exemplo que estende o WCF, e utiliza a classe GZipStream (System.IO.Compression) para compactar as mensagens; além deste exemplo, há um projeto chamado WCF Extensions, que possui essa funcionalidade de compactação. É importante dizer que em ambos os lados (cliente e serviço) precisam acoplar o código para efetuar a compactação e descompactação, caso contrário, a mensagem não poderá ser lida.

Além disso, para aqueles que hospedam o serviço no IIS, podem tirar proveito da compactação que ele faz. O IIS fornece um serviço que permite efetuar a compactação de conteúdo dinâmico, que é o caso de serviços WCF. Quando habilitado, este serviço irá compactar a resposta que será enviada ao cliente. O IIS compacta a resposta dependendo de um header que vem na requisição, indicando se o cliente consegue ou não interpretar o conteúdo compactado. Como os bindings do WCF não fornecem isso, você tem que explicitamente adicionar um header para encaminhar ao servidor (IIS) que você consegue compreender o algoritmo GZIP ou o Deflate. Esse header é o Accept-Encoding, e para fazer isso podemos recorrer ao código abaixo:

using (ServiceClient p = new ServiceClient())
{
    using (new OperationContextScope(p.InnerChannel))
    {
        var props = new HttpRequestMessageProperty();
        props.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");
        OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = props;

        Console.WriteLine(p.RetornaUmTextoMuitoGrande());
    }
}

Ao monitorar a requisição através do Fiddler, você poderá perceber que essas informações são encaminhadas ao IIS, incluindo o header necessário:

POST /MeuServico/Service.svc HTTP/1.1
Content-Type: text/xml; charset=utf-8
Accept-Encoding: gzip,deflate
SOAPAction: "http://tempuri.org/IService/RetornaUmTextoMuitoGrande"
Host: israelaecenb1
Content-Length: 151
Expect: 100-continue
Connection: Keep-Alive

Com isso, a mensagem de retorno que antes tinha um total de 2.000.176 bytes, passa a ter apenas 17.494 bytes usando o GZIP. Mas apesar de conseguirmos visualizar o conteúdo compactado, isso não quer dizer o proxy, ou melhor, o binding que está do lado do cliente, irá conseguir interpretar o mesmo. Como comentado acima, pelo fato do WCF não suportar nativamente a compactação, uma exceção do tipo ProtocolException será disparada, indicando exatamente isso.

Na versão 4.0 do WCF, uma nova propriedade foi adicionada na classe HttpTransportElement, chamada de DecompressionEnabled. Essa propriedade recebe um valor boleano indicando se o WCF pode ou não compactar as mensagens de resposta que são enviadas ao cliente. Por padrão, essa propriedade é definida como True, mas para conseguir acessá-la, é necessário a criação de um binding customizado. Um outro ponto importante é com relação à mensagem de requisição, que ao contrário das versões anteriores, a versão 4.0 do WCF já embuti o header necessário (Accept-Encoding) para que o serviço saiba que o cliente consegue descompactar a resposta.

Tags: , , , ,

WCF

UserNames com autenticação Basic

by Israel Aece 3. December 2009 07:51

Há algum tempo em comentei aqui sobre as opções de segurança no WCF, e mais tarde, como customizá-la para receber e validar usernames e passwords em um repositório qualquer. Antes da versão 3.5 do WCF, essa customização somente trabalha com segurança em nível de mensagem. Com o 3.5, a Microsoft adicionou esse suporte para também trabalhar com segurança em nível de transporte.

Isso é útil em cenários onde queremos utilizar a autenticação Basic do HTTP como meio de fornecimento das credenciais, mas ao invés da conta informada existir e de ser validada dentro do sistema operacional, vamos customizar essa regra, utilizando um repositório customizado, como uma base de dados, evitando que os clientes tenham, obrigatoriamente, contas cadastradas dentro do Windows.

Para que isso funcione, a configuração de segurança do binding deverá ser definida como Transport, e a forma de fornecimento de credenciais como Basic. Além disso, você também precisará implementar um validador customizado, herdando da classe abstrata UserNamePasswordValidator, como eu já mostrei neste artigo. Abaixo temos a configuração do lado do serviço:

<bindings>
  <basicHttpBinding>
    <binding name="bindingConfig">
      <security mode="Transport">
        <transport clientCredentialType="Basic"/>
      </security>
    </binding>
  </basicHttpBinding>
</bindings>

Apenas lembre-se de que o Basic faz com que as credenciais sejam trafegadas codificadas e não criptografadas, e justamente por isso, estamos recorrendo à segurança em nível transporte (que neste caso é o HTTPS), para garantir a integridade e confidencialidade da mensagem e das credenciais. Para informar as credenciais do lado do cliente, você faz da mesma forma que antes:

using (Servico p = new Servico)
{
    p.ClientCredentials.UserName.UserName = "IA";
    p.ClientCredentials.UserName.Password = "123";

    Console.WriteLine(p.Ping("teste"));
}

O único problema desta técnica, é que você não poderá hospedar o serviço no IIS. Isso se deve ao fato de que quando o atributo clientCredentialType estiver definido como Basic, o IIS fará a verificação durante o carregamento do serviço, analisando se o modo de autenticação Basic está habilitado para aquele diretório virtual, e caso contrário, uma exceção do tipo NotSupportedException será disparada, informando que o modo Basic não está habilitado.

Só que se você habilitá-lo, o problema volta a acontecer, pois a ideia por trás desta técnica, é utilizar o Basic como forma de envio das credenciais, mas não de validá-las, e com isso, se você colocar um usuário e senha que não existe dentro do Windows, ele não deixará você acessar o serviço, e mesmo que você digite um usuário/senha válidos, a implementação da classe UserNamePasswordValidator nunca será executada. Para que isso seja possível, você precisa modificar o pipeline do ASP.NET, acoplando módulos customizados que farão esse trabalho.

Tags: , ,

Security | WCF

Consumindo serviços via HTTPS

by Israel Aece 16. October 2009 07:26

Quando você instala um certificado em um servidor Web, ele poderá ser utilizado por alguma aplicação (UI ou serviços) que é executada no mesmo. Ao vincular esse certificado através do binding de HTTPS no IIS, você poderá começar a fazer uso deste protocolo na sua aplicação. Agora, imagine que você tenha um certificado que foi emitido para um outro domínio, diferente daquele que você está utilizando. Por exemplo, veja que o certificado abaixo foi emitido para o "Dummy", diferente do meu domínio utilizado pela aplicação, que é localhost.

Ao tentar acessar através do navegador, uma mensagem de aviso será exibida, indicando que o certificado que ele está utilizando é duvidoso, já que foi emitido para um domínio diferente daquele que ele foi vinculado. A imagem abaixo mostra essa mensagem:

Quando o usuário é interrogado, ele poderá decidir se continua ou não acessando aquele site, algo simples de fazer quando trata-se de aplicações que permitem a interação do usuário. Podemos também hospedar serviços no IIS, sejam eles WCF ou ASP.NET Web Services (ASMX), expondo através de HTTPS. Depois deste serviço referenciado na aplicação cliente, ao executá-la, você poderá se deparar com uma exceção do tipo SecurityNegotiationException, com a seguinte mensagem: Could not establish trust relationship for the SSL/TLS secure channel with authority [Servidor].

Durante a execução da aplicação cliente, antes dela efetivamente consumir o serviço, o WCF pergunta se ele deve ou não confiar no certificado, e o comportamento padrão é não. É exatamente o que acontece quando acessado através do navegador. Como o serviço não é interativo, você precisa de alguma forma, verificar se deseja ou não confiar naquele certificado que está sendo utilizado. Se não pudermos alterar o certificado, de uma forma semelhante a que vimos aqui, podemos escrever um código que permita efetuar essa verificação em tempo de execução.

Para isso, fazemos o uso da classe estática ServicePointManager, que está no namespace System.Net. Essa classe é responsável por gerenciar as conexões com a internet. Essa classe fornece uma propriedade chamada ServerCertificateValidationCallback, que deverá apontar para um método que segue a assinatura imposta pelo delegate RemoteCertificateValidationCallback. Abaixo temos um exemplo da sua utilização:

ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;

A partir do momento que você configura esta propriedade, as requisições HTTPS que são realizadas, começam a ser verificadas pelo método vinculado. Repare que entre os parâmetros enviados para este método, um deles é o certificado que está sendo avaliado. E além disso, esse método deverá retornar um valor boleano, indicando se o certificado é ou não válido, de acordo com a regra que você especificar ali. É importante que você analise cuidadosamente antes de especificar isso no seu código, pois dessa forma, ele dará "bypass" em todos os certificados, independentemente se eles forem ou não válidos. Talvez isso é algo que seja legal em ambiente de desenvolvimento, mas em produção poderá representar uma vulnerabilidade de segurança.

Tags: , ,

WCF

Powered by BlogEngine.NET 1.5.0.0
Theme by Mads Kristensen

Sobre

Meu nome é Israel Aece e sou especialista em tecnologias de desenvolvimento Microsoft, atuando como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. [ Mais ]

Twitter

Host