Acessando serviços de forma relativa

by Israel Aece 20. July 2010 22:36

Como sabemos, aplicações Silverlight podem recorrer a recursos que rodam no servidor que a hospeda utilizando WCF. Com isso, a aplicação que serve como host para o serviço é uma aplicação ASP.NET tradicional (WebForms ou MVC, Web Site ou Web Application), onde efetivamente criaremos o serviço WCF para que servirá a aplicação Silverlight.

Uma vez com o serviço pronto, tudo o que precisamos fazer para consumir o serviço no Silverlight, é a referência para o mesmo, utilizando a opção Add Service Reference, fornecida pela IDE do Visual Studio .NET. Como é feito em qualquer aplicação cliente, o Visual Studio extrairá as informações do documento WSDL e criará o proxy do lado do cliente, incluindo um arquivo chamado ServiceReferences.ClientConfig, que conterá as configurações de acesso ao serviço referenciado. O código abaixo ilustra esse arquivo:

<configuration>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IContrato"
                 maxBufferSize="2147483647"
                 maxReceivedMessageSize="2147483647">
          <security mode="None" />
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://localhost:1551/ServicoDeTeste.svc"
                binding="basicHttpBinding"
                bindingConfiguration="BasicHttpBinding_IContrato"
                contract="Servico.IContrato"
                name="BasicHttpBinding_IContrato" />
    </client>
  </system.serviceModel>
</configuration>

Como podemos notar, endereço do serviço WCF está definido de forma absoluta. Isso implica em alguns problemas que podemos ter durante a distribuição/instalação do projeto no servidor. Esse arquivo é comprimido para dentro de um outro arquivo, com extensão *.xap, e que por sua vez, é depositado no diretório ClientBin do projeto ASP.NET que hospeda a aplicação Silverlight. Ao colocar isso no servidor onde ele deverá rodar, provavelmente o endereço do serviço mudará, e ficará complicado e trabalhoso para alterar.

Para melhorar isso, a versão 4.0 do Silverlight permite especificar, de forma relativa, o endereço para o serviço, ou seja, não precisamos mais definir o endereço completo até o arquivo *.svc. Apesar da IDE ainda não ser inteligente o bastante para utilizar esse recurso ao referenciar o serviço, nada nos impede de alterar isso diretamente no arquivo de configuração do lado do cliente, apontando relativamente para o serviço WCF. Para o teste, eu criei um serviço na raiz da aplicação ASP.NET, e como a aplicação Silverlight é colocada na pasta ClientBin desta mesma aplicação, tudo o que precisamos fazer é subir um nível (../). Abaixo podemos visualizar essa ligeira mudança, mas que traz uma grande flexibilidade.

<configuration>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IContrato"
                 maxBufferSize="2147483647"
                 maxReceivedMessageSize="2147483647">
          <security mode="None" />
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="../ServicoDeTeste.svc"
                binding="basicHttpBinding"
                bindingConfiguration="BasicHttpBinding_IContrato"
                contract="Servico.IContrato"
                name="BasicHttpBinding_IContrato" />
    </client>
  </system.serviceModel>
</configuration>

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

Web.config e a reinicialização da aplicação

by Israel Aece 12. April 2010 10:29

Recentemente fui questionado sobre a possibilidade de alteração de um arquivo de configuração para a adição de uma nova string de conexão com uma determinada base de dados. Como sabemos, podemos utilizar a seção <connectionStrings /> para isso, onde elencamos todas as conexões que possam ser utilizadas pela aplicação.

O grande problema disso, é que ao modificar o arquivo Web.config, a aplicação é reinicializada, e com isso todas as informações que são armazenadas na memória são perdidas. Isso quer dizer que membros estáticos, variáveis de aplicação, de sessão e caching são descartadas, causando problemas dentro da aplicação, e trazendo péssimas experiências aos usuários.

Para contornar esse problema, podemos recorrer à uma técnica que nos permite isolar as configurações de uma seção inteira em um outro arquivo de configuração. No nosso exemplo, iremos criar um arquivo chamado connectionStrings.config contendo apenas a seção <connectionStrings />, assim como poder notar abaixo:

<connectionStrings>
  <add name="SqlConnectionString" connectionString="..."/>
  <add name="OracleConnectionString" connectionString="..."/>
</connectionStrings>

Já no arquivo de configuração da aplicação, Web.config, referenciamos este arquivo recém criado, e para isso utilizamos o atributo configSource, apontando fisicamente para ele:

<connectionStrings configSource="connectionStrings.config" />

Só que isso somente não resolverá. Como estamos querendo evitar a reinicialização caso alguma alteração na seção <connectionStrings /> aconteça, precisamos definir o atributo restartOnExternalChanges como False, que por padrão é True. Quando é definido como False e temos essa seção em um segundo arquivo, a aplicação não será reinicializada. Essa configuração deve ser aplicada durante a criação da seção, no arquivo machine.config que está localizado dentro do diretório de instalação do .NET Framework. Abaixo temos a seção já configurada com este atributo:

<section
    name="connectionStrings"
    type="System.Configuration.ConnectionStringsSection, ..."
    restartOnExternalChanges="false"
    requirePermission="false" />

Com isso, como já era de se esperar, qualquer mudança que ocorra no arquivo connectionStrings.config não provocará a reinicialização da aplicação, mas as alterações já serão detectadas por ela. O único cuidado que você precisa ter aqui é quando há alguma espécie de caching sendo utilizada pela aplicação ao pelo runtime do ASP.NET, pois as mudanças não irão refletir para a mesma até que ela seja reinicializada. Essa técnica também é válida para seções customizadas.

Tags:

.NET Framework | ASP.NET

Autenticação via Claims no ASP.NET WebForms

by Israel Aece 12. March 2010 23:09

Nos artigos anteriores, falei sobre a proposta da Microsoft para tentar unificar o sistema de autenticação das aplicações. Nos dois primeiros artigos, comentei um pouco sobre os problemas existentes e a teoria que circunda qualquer Identity MetaSystem. Na sequência, introduzi o WIF e alguns tipos que são comuns para qualquer tipo de aplicação que fará uso deste novo framework. A finalidade deste artigo é mostrar como podemos aplicar o WIF em uma aplicação ASP.NET WebForms, analisando tudo o que é necessário para colocar isso em funcionamento, incluindo a criação de um STS para testes.

Quando efetuamos a instalação do SDK do WIF, várias configurações são realizadas dentro da IDE do Visual Studio .NET para suportá-lo. Entre essas configurações, temos a adição de novas templates de projetos, que nos permite criar um projeto com o WIF já pré-configurado, evitando assim lidarmos diretamente com as configurações de baixo nível que ele exige e que veremos no decorrer deste artigo. As quatro templates de projetos disponíveis, estão listadas abaixo com suas respectivas descrições:

  • WCF Security Token Service: Template que traz a configuração padrão para criar um serviço WCF que servirá como um STS no ambiente ativo.
  • ASP.NET Security Token Service Web Site: Template que traz a configuração padrão para criar uma aplicação ASP.NET que servirá como um STS no ambiente passivo.
  • Claims-aware WCF Service: Template que traz um serviço WCF pré-configurado para utilizar algum STS, que será responsável pela autenticação do usuário.
  • Claims-aware ASP.NET Web Site: Template que traz uma aplicação ASP.NET pré-configurada para utilizar algum STS, que será responsável pela autenticação do usuário.

Como disso no parágrafo anterior, o artigo será focado em algumas partes dos bastidores do WIF dentro de uma aplicação ASP.NET (ambiente passivo), e para começar, sabemos que podemos utilizar o atributo mode do elemento authentication no arquivo Web.config, para determinar qual será o modelo de autenticação; esse atributo permite escolhermos um entre as quatro opções disponíveis: None, Forms, Windows e Passport. Abaixo temos um exemplo desta configuração:

<configuration>
  <system.web>
    <authentication mode="Windows" />
  </system.web>
</configuration>

Ao configurar desta forma, automaticamente a aplicação ASP.NET reutilizará a credencial do usuário que está atualmente na máquina, e utilizará essa credencial para refinar o acesso (autorização) e para saber quem o usuário realmente é. Com isso, as propriedades Thread.CurrentPrincipal e HttpContext.User retornam uma instância da classe WindowsPrincipal, que foi criada pelo runtime do ASP.NET, e que corresponde ao usuário atual. Para nos certificarmos disso, podemos recorrer ao seguinte código:

WindowsPrincipal wp = HttpContext.Current.User as WindowsPrincipal;
WindowsIdentity wi = wp.Identity as WindowsIdentity;
Response.Write(wi.Name);

Da mesma forma, se utilizarmos a opção Forms, o runtime do ASP.NET criará uma instância da classe GenericPrincipal e FormsIdentity, que corresponderão ao usuário autenticado através do Forms Authentication, e que na maioria da vezes, esta opção recorre à uma base de dados que serve como repositório para os usuários, onde os serviços de MembershipProvider e de RoleProvider, fornecidos a partir da versão 2.0 do ASP.NET, são uma das várias alternativas disponíveis.

Mas e se queremos preparar nossa aplicação para suportar claims? No artigo Explorando o WIF, eu comentei sobre as interfaces IClaimsIdentity e IClaimsPrincipal, e são elas que devem ser utilizadas pelas aplicações baseadas em claims. Da mesma forma que vimos anteriormente, a criação de classes que implementam essas interfaces ainda continua sendo de responsabilidade do runtime do ASP.NET, que ao receber o token do STS, fará tudo o que for necessário para ler o seu conteúdo e criar os objetos necessários, para que assim determine se o usuário foi devidamente autenticado. Falaremos sobre estes passos mais adiante, ainda neste artigo.

Mesmo que no primeiro momento não queremos envolver um STS para efetuar a validação do usuário, podemos já fazer com que nossa aplicação transforme tudo o que temos atualmente em claims. Por exemplo, se minha aplicação usa autenticação baseada no Windows, então eu menciono WindowsPrincipal na aplicação; já se minha aplicação possui autenticação baseada em Forms, então poderei mencionar GenericPrincipal. Ao invés de lidar diretamente com esses tipos específicos, podemos recorrer as interfaces IClaimsIdentity e IClaimsPrincipal, quais foram desenhadas para atender a essa necessidade, ou seja, independem de tecnologia de autenticação que está sendo utilizada.

Para fazer com que o ASP.NET passe a criar as classes de identity e principal que fazem uso de claims, precisamos acoplar no pipeline do mesmo, um módulo fornecido pelo WIF chamado de ClaimsPrincipalHttpModule, que está debaixo do namespace System.IdentityModel.Web. É importante dizer que os tipos que veremos a partir daqui, estão dentro do assembly Microsoft.IdentityModel.dll, que deverá ser referenciado nas aplicações. Este módulo se vincula ao evento PostAuthenticateRequest (que ocorre assim que o usuário foi identificado), e faz uma espécie de tradução, tranformando o principal e identity corrente, em objetos que implementam as interfaces IClaimsIdentity e IClaimsPrincipal, respectivamente. Durante essa transformação, todos os atributos que eram fornecidos pela tecnologia de autenticação, passam a virar claims, e estão disponívieis para utilização. Para exemplificar o que vimos acima, o primeiro passo é adicionar o módulo no pipeline do ASP.NET, utilizando o elemento httpModules:

<configuration>
  <system.web>
    <authentication mode="Windows" />
    <httpModules>
      <add name="ClaimsPrincipalHttpModule"
           type="Microsoft.IdentityModel.Web.ClaimsPrincipalHttpModule, ..." />
    </httpModules>
  </system.web>
</configuration>

Essa configuração fará com que o ASP.NET faça a transformação do que foi gerado pela tecnologia de autenticação (Windows ou Forms), em tipos que correspondem ao modelo de claims. Sendo assim, o código que utilizamos acima para extrair o nome do usuário a partir da propriedade User da classe HttpContext, pode ser substituído pelo código abaixo. O interessante é que independentemente de que modelo de autenticação estamos utilizando, esse código não irá variar.

IClaimsPrincipal principal = HttpContext.Current.user as IClaimsPrincipal;
IClaimsIdentity identity = principal.Identity as IClaimsIdentity;

foreach (var item in identity.Claims)
    Response.Write(string.format("{0}: {1}<br />", item.ClaimType, item.Value));

Envolvendo um STS

No exemplo que vimos acima, ele somente se preocupa em fazer com que todos os modelos de autenticação suportados pelo ASP.NET, sejam automaticamente traduzidos para claims, nada mais. Até então, a autenticação continua sob responsabilidade desta mesma aplicação. Como podemos proceder para "externalizar" a autenticação? Será justamente este assunto que está seção irá abordar, entendendo como configurar a aplicação para conseguir receber os tokens que serão gerados por um terceiro, o STS.

Em uma aplicação ASP.NET tradicional, e que faz uso do modelo Forms para autenticação, quando tentamos acessar uma página protegida e não estamos autenticado, o ASP.NET automaticamente irá nos redirecionar para uma página de login que é definida no arquivo Web.config, colocando na Url uma query string chamada ReturnUrl, que contém a página que tentamos acessar. Esse parâmetro é interessante, pois se formos devidamente autenticado, seremos redirecionado para ela.

Quando envolvemos um STS, o processo é bem semelhante, mas ao invés dele redirecionar para uma página local que efetuará o login, devemos ser redirecionados para um outro site, que por sua vez, fará a validação e também a emissão do token, redirecionando o usuário para aplicação - protegida - que ele tentou acessar. Como vimos nos artigos anteriores, esse é o ambiente passivo, onde o navegador irá atuar apenar para coordenar os redirecionamentos entre as aplicações (STS e relying party). A imagem abaixo ilustra como o processo deverá acontecer:

Analisando a imagem acima, repare que o usuário solicita uma aplicação ASP.NET que está protegida (1). Essa aplicação verifica que o usuário não está autenticado. Como ela não é mais responsável por autenticá-lo, ela redireciona o usuário para o STS em que ela confia (2). O usuário se autentica no STS utilizando a tecnologia que foi definida lá, que pode ser Kerberos (Windows), UserName (Forms Authentication), certificados, etc. (3). Se o usuário for válido, um token é emitido para aquele usuário (4). Finalmente, o navegador redireciona o usuário para a página solicitada, mas agora incluindo o token (5).

Toda a mágica que acontece neste processo, acaba sendo realizada através de funcionalidades que o próprio protocolo HTTP fornece, ou seja, faz uso de requisições GET e POST, anexando na URL alguns parâmetros que são utilizados para garantir que tudo funcione conforme o esperado. Antes de analisarmos o conteúdo que é trafegado entre as partes, vamos nos concentrar em como devemos configurá-las para que essa conversação seja possível.

Antes de criar a aplicação que servirá como relying party, vamos optar por iniciar pelo STS. No início do artigo, vimos as templates de projeto que são criadas na instalação do SDK do WIF, e uma delas é justamente uma aplicação ASP.NET que serve como um STS (ASP.NET Security Token Service Web Site). A ideia desta template, é permitir você criar uma aplicação que valida usuários e emita tokens para ele. Um cenário em que ele poderia ser utilizado, é se você tiver uma estrutura de Membership, Roles e Profile, e quer que este STS valide os usuários e faça a emissão de tokens baseando-se nessa estrutura. Como sabemos, a vantagem disso é que todas as aplicações podem confiar neste STS, centralizando assim toda a lógica necessária para isso.

A aplicação STS gerada pela template, possui três arquivos na raiz do projeto: Default.aspx, Login.aspx e Web.config. No arquivo Web.config, não há nada especial, nada que não conhecemos. A configuração inicial faz com que o atributo mode do elemento authentication seja definido como Forms, pois será esse o modo de autenticação que os usuários usarão lá. Neste mesmo arquivo de configuração, a página Login.aspx é definida como a página onde os usuários deverão informar suas credenciais, e a página Default.aspx é proibida de ser acessada por usuários anônimos.

<configuration>
  <system.web>
    <authentication mode="Forms">
      <forms loginUrl="~/Login.aspx" />
    </authentication>
    <authorization>
      <deny users="?" />
    </authorization>
  </system.web>
</configuration>

Além do que vimos acima, dentro do projeto criado para servir como STS, ele ainda fornece o diretório App_Code, com algumas classes dentro. As classes que constam ali, principalmente a CustomSecurityTokenService, é o nosso STS em si. Repare que ele herda diretamente da classe SecurityTokenService, que fornece toda a estrutura necessária para a criação de um STS customizado. Ele fornece dois métodos chaves que devem ser sobrescritos nas classes derivadas, a saber: GetScope e GetOutputClaimsIdentity.

O primeiro método, GetScope, nos dá a possibilidade de verificar quais são as chaves de criptografia que será utilizada, baseando-se no endereço da relying party. Esse método retorna uma instância da classe Scope, que contém toda a configuração necessária para gerar o retorno para a respectiva aplicação (relying party). Já o segundo método, GetOutputClaimsIdentity, é o local onde efetivamente definimos quais claims serão criadas para aquele usuário recém autenticado, e que na maioria das vezes, iremos recorrer a uma busca no banco de dados para extrair esses dados. Esse método retorna a instância de uma classe ClaimsIdentity, que implementa a interface IClaimsIdentity, e como sabemos, fornece uma propriedade chamada Claims, que nada mais é que uma coleção, onde podemos adicionar quantas claims desejarmos.

Ambos métodos recebem como parâmetro um principal, do tipo IClaimsPrincipal, que corresponde ao principal criado para o chamador do STS, e além dele, um outro parâmetro chamado request, do tipo RequestSecurityToken, que representa a requisição enviada ao STS (RST), e que pode ser utilizada para extrair informações adicionais.

Note que a validação do usuário acaba sendo realizada através da tecnologia que utilizamos para isso, que neste caso, é a Forms Authentication. No code-behind do arquivo Login.aspx, temos a validação acontecendo, que deve recorrer à alguma base de dados, como o MembershipProvider, para validar o usuário. Somente a partir daí é que a classe do STS que vimos acima entrará em cena.

Criação da Relying Party

Na seção anterior, expliquei como estruturar a aplicação que servirá como STS. Uma vez que ela está criada, precisamos agora desenvolver as aplicações que servirão como relying party, ou melhor, aplicações que utilizarão aquele STS para autenticar o usuário. Da mesma forma que fizemos para criar um STS, vamos recorrer a a template de projeto chamada Claims-aware ASP.NET Web Site para criar a relying party. Este projeto já traz a estrutura necessária para que a aplicação já consuma um STS, mas como fizemos acima, vamos entender a mágica que está por trás disso tudo.

Do lado da aplicação ASP.NET que será uma relying party, pequenas mudanças são necessárias no arquivo de configuração (Web.config), mas que impactam diretamente em como o runtime do ASP.NET irá tratar os redirecionamentos necessários. Da mesma forma que já fazemos no ASP.NET tradicional, precisamos negar o acesso aos usuários anônimos, aquelas páginas que necessitam que eles estejam devidamente autenticados. Para isso, utilizamos o elemento authorization, assim como é mostrado abaixo:

<configuration>
  <system.web>
    <authentication mode="None" />
    <authorization>
      <deny users="?" />
    </authorization>
  </system.web>
</configuration>

Note também que a autenticação é desligada nesta aplicação, pois não compente mais a ela validar o usuário. Mas isso por si só não é suficiente para que esta aplicação contate o STS. Ainda é necessário especificar algumas outras informações que conduzirá a relying party à como efetuar o redirecionamento para o STS, e para isso, há uma seção exclusiva do WIF, que nos permite configuar cada um dos parâmetros exigidos. O código abaixo ilustra a configuração de uma relying party que confia no STS que criamos acima:

<microsoft.identityModel>
  <service>
    <audienceUris>
       <add value="https://aplicacoes.minhaempresa.com.br/App01/" />
    </audienceUris>
    <applicationService>
       <claimTypeRequired>
          <claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
                     optional="true" />
          <claimType type="http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
                     optional="true" />
       </claimTypeRequired>
    </applicationService>
    <federatedAuthentication>
      <wsFederation passiveRedirectEnabled="true"
                    issuer="https://administrativo.minhaempresa.com.br:444/STS01/"
                    realm="https://aplicacoes.minhaempresa.com.br/App01/"
                    requireHttps="true" />
      <cookieHandler requireSsl="true" />
    </federatedAuthentication>
    <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, ...">
      <trustedIssuers>
        <add thumbprint="0844D588E25237D81AC61BF7C38EDAB7075BD024"
             name="https://administrativo.minhaempresa.com.br:444/STS01/" />
      </trustedIssuers>
    </issuerNameRegistry>
  </service>
</microsoft.identityModel>

Primeiramente precisamos registrar a seção microsoft.identityModel no arquivo de configuração, através do elemento configSections, pois ela não consta no schema de configuração do .NET Framework. Uma vez registrada, podemos fazer uso dela na mesma aplicação. Imediatamente dentro desta seção, temos uma sub-seção chamada de service que envolverá todas as configurações de autenticação daquela aplicação.

A partir de agora, temos outras sub-seções que efetivamente configuram o comportamento do WIF, e como podemos ver, temos uma seção chamada audienceUris. Dentro desta seção, podemos elencar uma coleção de URIs, que definem os possíveis endereços em que o token poderá ser entregue, evitando que clientes maliciosos façam uso do mesmo. Já o próximo elemento, applicationService/claimTypeRequired, tem um papel importante, que diz ao STS quais claims a aplicação necessita, podendo você determinar, através do atributo optional, se ela é ou não opcional.

Em seguida, vemos um elemento chamado federationAuthentication. Este elemento nos permite configurar as informações referente ao STS em que a aplicação confia. O sub-elemento wsFederation fornece alguns atributos que nos auxiliam nisso. O primeiro deles é o issuer, onde devemos especificar o endereço do STS; já no atributo realm informamos o endereço da aplicação solicitante, e que coincide com o endereço especificado no elemento audienceUris. E ainda, temos o atributo passiveRedirectEnabled, que recebe um valor boleano indicando se o WIF deve efetuar o redirecionamento automático quando detectar que o usuário não está autenticado. Veremos um cenário mais claro para este atributo quando falarmos dos controles fornecidos pelo WIF. Ainda debaixo do elemento federationAuthentication, temos o sub-elemento chamado de cookieHandler, que fornece alguns atributos para configurar o cookie que corresponderá a sessão de segurança de um determinado usuário.

Para finalizar este grande bloco de configuração, temos ainda uma última seção, chamada de issuerNameRegistry. Como o próprio nome diz, permite especificarmos ali os STSs em que a aplicação confia. É importante dizer que cada STS (issuer) é conhecido através do thumbprint de um certificado X.509. Note também que esta seção especifica o tipo da classe que é responsável por efetuar a validação do STS. A classe é definida através do atributo type, e no exemplo acima, a classe que utilizamos já está embutida no WIF, e é chamada de ConfigurationBasedIssuerNameRegistry, que tem a finalidade de extrair do arquivo de configuração as informações necessárias para efetuar tal validação. Caso queira customizar isso, basta você herdar da classe abstrata IssuerNameRegistry, sobrescrevendo o método GetIssuerName.

Depois de todas as configurações que vimos acima, isso ainda não funciona sozinho, pois ainda não há nada que mude a execução padrão do ASP.NET. É justamente aqui que introduzimos duas novas classes, chamadas de WSFederationAuthenticationModule e SessionAuthenticationModule (namespace Microsoft.IdentityModel.Web). Ambas classes são módulos (implementam a interface IHttpModule), e devem ser acoplados na execução do ASP.NET, para que o WIF entre em ação. Esses módulos irão consumir grande parte das configurações que vimos acima, para que consiga efetuar todas as tarefas, tais como: redirecionamento, criptografia e deserialização do token gerado pelo STS.

Uma vez que o módulo WSFederationAuthenticationModule é adicionado ao pipeline do ASP.NET, ele se vincula ao evento AuthenticateRequest. Ao tentar acessar uma página protegida e não estiver autenticado, esse módulo irá detectar que o acesso foi negado (status 401 do HTTP), e redirecionará o usuário para o respectivo STS. Depois que o usuário for validado e o STS emitir o token para ele, novamente ele será redirecionado novamente para a aplicação (RP), onde este mesmo módulo interceptará e irá utilizar este token para criar a instância da classe IClaimsPrincipal, atribuindo-a a propriedade User da classe HttpContext. Esse módulo encapsula todas as complexidades dos padrões WS-* que são utilizados para efetuar a comunicação entre a aplicação e o autenticador.

Se a cada página que tentássemos acessar fossemos redirecionado para o STS, isso seria algo totalmente inviável. Sendo assim, é necessário que uma vez autenticado, essa sessão seja mantida durante as requisições que fazemos para esta mesma aplicação. É justamente neste momento que entra em cena o módulo SessionAuthenticationModule. A finalidade dele é monitorar o cookie que é criado e representa o usuário que foi autenticado pelo STS. Caso esse cookie esteja presente e seja válido, esse módulo faz o trabalho necessário para manter a propriedade User da classe HttpContext configurada, sem precisar efetuar - novamente - a visita ao STS. Abaixo temos os dois módulos adicionados (elemento httpModules) ao runtime do ASP.NET na relying party:

<httpModules>
  <add name="WSFederationAuthenticationModule"
       type="Microsoft.IdentityModel.Web.WSFederationAuthenticationModule, ..." />
  <add name="SessionAuthenticationModule"
       type="Microsoft.IdentityModel.Web.SessionAuthenticationModule, ..." />
</httpModules>

É importante dizer que além destes módulos modificarem a execução de uma aplicação ASP.NET, eles fornecem uma série de membros (métodos, eventos e propriedades) que podem ser utilizados pela aplicação, mas esses membros serão abordados em um futuro artigo.

Mas estes dois módulos não são os únicos elementos que são executados durante o processo de autenticação do usuário. Internamente eles fazem uso de diversas outras classes que possuem um papel importante ou que servem apenas como um ponto de estensibilidade. Para ter uma visão mais clara disso, analise a imagem abaixo, que foi baseada em uma apresentação do Vittorio Bertocci durante o PDC 2009:

A classe SecurityTokenHandler (namespace Microsoft.IdentityModel.Tokens) que vemos depois do módulo WSFederationAuthenticationModule, é uma classe abstrata e serve como base para outros token handlers. Token handler, como o próprio nome diz, é o responsável por validar o token gerado pelo STS, que dependendo do tipo de autenticação utilizado, o token pode estar formatado de uma maneira/versão específica. Como essa classe é abstrata, há várias implementações dela debaixo deste mesmo namespace, onde cada implementação visa um formato diferente (Saml, UserName, etc.), e o qual será autenticado depende da requisição.

Na sequência visualizamos a classe ClaimsAuthenticationManager. Por padrão, essa classe não faz absolutamente nada, mas dá a chance de customizá-la através do método virtual Authenticate, permitindo interceptar a primeira requisição, onde o token gerado pelo STS é enviado para a aplicação. Com ela, podemos customizar e validar as claims que foram retornadas pelo STS, fazendo alguma validação, adicionando novas claims ou até mesmo fazendo transformações/traduções.

Finalmente, temos a classe ClaimsAuthorizationManager, que permite definir um lugar centralizado para definir as regras de autorização. Essa classe também fornece um método chamado CheckAccess, que retorna uma valor boleano indicando se o acesso será ou não concedido. Para poder tomar a decisão para conceder ou negar o acesso, este método recebe como parâmetro uma instância da classe AuthorizationContext, que contém os seguintes membros: Action, Principal e Resource. Estensibilidade é um assunto longo e complexo, e que não será abordado neste artigo, pois exige um artigo exclusivo para isso.

Assistente para Configuração

A ideia de toda a explicação acima foi tentar mostrar os passos necessários para configurar, manualmente, tudo o que é necessário para acoplar o WIF no ASP.NET, abordando os principais elementos que devem estar presentes nos arquivos de configuração da relying party e do STS. Só que configurar isso manualmente pode ser complexo e propício a erros, pois devido a quantidade de informações que devemos definir, eventualmente podemos esquecer de algo importante, que evitará do WIF funcionar adequadamente.

Para tornar a configuração de ambas as partes simples e intuitiva, a Microsoft disponibiliza um utilitário chamado Federation Utility (FedUtil.exe), que é instalado juntamente com o SDK do WIF. Esse assistente tem a finalidade de nos guiar durante a configuração de uma relying party. Quando instalamos o SDK, ao clicar com o botão direito do mouse em cima de um projeto ASP.NET, uma nova opção está disponível, chamada Add STS Reference. O utilitário é inicializado a partir desta aplicação, e nos ajudará a escolher/criar um STS. As imagens abaixo ilustram os dois primeiros passos disponíveis:

Na primeira imagem definimos o caminho físico até o arquivo Web.config, e logo abaixo temos a URI desta mesma aplicação (relying party). O passo a seguir, que está ilustrado na imagem subsequente, oferece três opções para a configuração do STS, e dependendo da opção escolhida, refletirá em configurações específicas no arquivo Web.config mencionado no primeiro passo. Abaixo temos a lista com as três opções e suas respectivas descrições:

  • No STS: Ao selecionar esta opção, tudo o que será feito na aplicação é a inserção do módulo ClaimsPrincipalHttpModule, conforme discutimos no início do artigo. Como sabemos, isso habilitará que qualquer tipo de autenticação que seja utilizado no projeto, seja transformado em claims.
  • Create a new STS project in the current solution: Esta opção cria na mesma solução um projeto ASP.NET que servirá como um STS, e depois disso, já irá alterar o arquivo Web.config da relying party, fazendo com que ela confie neste STS recém criado.
  • Use an existing STS: Como podemos notar, essa opção permite especificarmos um endereço de um STS existente, em que a aplicação corrente irá confiar.

Avançado alguns passos deste assistente, há uma tela em que são listadas as claims oferecidas por aquele STS, tela qual você pode avaliar se aquele STS fornece as claims necessárias para a sua aplicação, permitindo você evitar a referência à este STS caso ele não atenda. A imagem abaixo exibe esta tela informativa:

Controles

Ao referenciar o assembly que corresponde ao WIF em uma aplicação ASP.NET, automaticamente dois controles são adicionados à toolbox do Visual Studio: FederatedPassiveSignInStatus e FederatedPassiveSignIn, onde ambos estão debaixo do namespace Microsoft.IdentityModel.Web.Controls. Assim como já acontece nos controles de autenticação do ASP.NET, o controle FederatedPassiveSignInStatus alterna a exibição de um HyperLink contendo o endereço para efetuar o login ou o logout, dependendo do status atual do usuário.

Recapitulando o que foi falado acima, quando o WIF detecta que o usuário não está autenticado, automaticamente irá redirecioná-lo para o STS, e tudo isso graças ao atributo passiveRedirectEnabled do elemento wsFederation, que quando estiver definido como True, terá esse comportamento. Mas imagine que você, por algum motivo, confie em mais do que um STS, ou ainda, quer dar a opção ao usuário dele se autenticar usando o STS ou um outro formato mais tradicional, como o próprio Membership. Neste caso, o redirecionamento não poderá acontecer automaticamente, e é neste momento que entra em cena o controle FederatedPassiveSignIn.

Neste caso, a relying party terá uma página que permitirá ao usuário escolher o modo de autenticação desejado, e o controle em questão, permitirá configurarmos todas as informações pertinentes ao STS que ele referencia. As suas principais propriedades são: Issuer e Realm, onde você deve definir o endereço do autenticar e da aplicação que o utiliza, respectivamente; ainda temos uma outra propriedade chamada SignInMode, que aceita duas opções expostas pelo enumerador SignInMode: Session e Single. A primeira opção (padrão) fará com que um cookie seja criado para persistir a sessão do usuário, enquanto a opção Single é utilizada apenas para uma única requisição. Abaixo temos o código ASPX correspondente ao controle FederatedPassiveSignIn, com as propriedades definidas, mas para que ele funcione de forma correta, é necessário definir o atributo passiveRedirectEnabled do elemento wsFederation como False.

<%@ Register
    Assembly="Microsoft.IdentityModel, ...."
    Namespace="Microsoft.IdentityModel.Web.Controls"
    TagPrefix="wif" %>

<wif:FederatedPassiveSignIn
    ID="FederatedPassiveSignIn1"
    runat="server"
    Issuer="https://administrativo.minhaempresa.com.br:444/STS01/"
    Realm="https://aplicacoes.minhaempresa.com.br/App01/"
    SignInMode="Session" />

Metadados

Para referenciar um serviço em uma aplicação, geralmente recorremos ao documento WSDL que ele fornece. Dentro deste documento há uma série de informações, que descrevem grande parte das capacidades do serviço, tais como: as operações fornecidas, o modelo de segurança, entre outras coisas. De forma análoga, o STS precisa informar para aquelas aplicações que desejam utilizá-lo, o que ele fornece, como por exemplo: endereço do mesmo para a validação do usuário, quais claims estão disponíveis, certificado usado para a proteção das mensagens que são trocadas, etc.

Todas essas informações são fornecidas através de um documento XML, localizado fisicamente no STS, e que todas as aplicações que desejam consumí-lo, podem e devem consultar esse arquivo para conhecer as informações necessárias para efetuar a configuração local (aquelaque encontra-se na seção microsoft.IdentityModel no arquivo Web.config). Ao rodar o utilitário FedUtil.exe, ele consulta esse documento para extrair as informações e efetuar a configuração necessária no arquivo Web.Config informado no primeiro passo do assistente.

Esse arquivo tem o nome de FederationMetadata.xml, e fica contido em uma pasta chamada FederationMetadata/2007-06, mas também é criado na aplicação que referencia o STS, com uma estrutura um pouco diferente. A ideia deste arquivo de metadado do lado do cliente, é fornecer ao STS o endereço da aplicação, podendo assim, o STS configurar as aplicações para quais ele poderá emitir tokens.

Estrutura da Requisição e Resposta

Quando utilizamos FormsAuthentication em um projeto ASP.NET, ao tentar acessar uma página protegida quando não estamos autenticados, somos redirecionados para a página de login especificada no Web.config, e um item chamado ReturnUrl é adicionado como query string, identificando a página que foi solicitada, para que se o usuário for autenticado, automaticamente será redirecionado para a mesma.

Ao utilizar o WIF, teremos algo semelhante, mas ao ser redirecionado para o STS que validará o usuário, vários parâmetros serão adicionados como query string, para que o STS possa utilizá-los durante o processo de validação, geração de tokens e, finalmente, o redirecionamento de volta para a aplicação. Esses parâmetros são definidos através da especificação do WS-Federation, que rege a estrutura das mensagens. Abaixo temos a lista dos possíveis parâmetros utilizados para efetuar a requisição (RST), com suas respectivas descrições:

  • wa: Este parâmetro identifica a action a ser executada pelo STS. Esse parâmetro pode receber dois valores, sendo o primeiro o wssignin1.0 para instruir o STS a a validar e gerar o token para o usuário. Já o segundo, wssignout1.0, diz ao STS para proceder o logout do usuário.
  • wtrealm: Representa a URI da aplicação que está solicitando a autenticação do usuário (relying party). O STS usará essa URI para identificar a aplicação solicitante e, eventualmente, efetuar alguma verificação interna, customizando as claims geradas para cada uma delas. Além disso, esse parâmetro serve também para que o STS saiba para onde deverá responder.
  • wctx: Este parâmetro define a URL original que o usuário tentou acessar, e assim como a ReturnUrl do ASP.NET, esse parâmetro serve para redirecionar o usuário para o local exato dentro da aplicação (relying party), depois da autenticação realizada.
  • wct: Este parâmetro indica o horário atual na aplicação solicitante, garantindo assim que o cálculo das eventuais expirações sejam feitas de forma coerente, já que o STS pode estar em um fuso, enquanto a relying party pode estar em outro.

Uma vez redirecionado, o STS fará o que for necessário para validar o usuário, e depois que isso for realizado, novamente o usuário será redirecionado devolta para a aplicação que ele tentou acessar. Neste momento, ao invés de ser uma requisição normal, através do método GET, um POST será efetuado. Isso se deve pelo fato de que o token gerado pode ser maior que 4KB, e isso é uma limitação imposta pelos navegadores na URL, impedindo que o método GET seja utilizado. Quando o POST é feito para a aplicação (relying party), o módulo WSFederationAuthenticationModule entra em cena, processando a resposta gerada pelo STS, como já vimos acima.

Para exemplificar, abaixo temos o endereço da requisição que foi montado pelo WIF, redirecionando o usuário para o STS referenciado com os parâmetros que identificam a relying party.

https://administrativo.minhaempresa.com.br:444/STS01/?
    wa=wsignin1.0&
    wtrealm=https%3a%2f%2faplicacoes.minhaempresa.com.br%2fApp01%2f&
    wctx=rm%3d0%26id%3dpassive%26ru%3d%252fApp01%252fDefault.aspx&
    wct=2010-03-12T00%3a55%3a37Z

Como vimos acima, uma vez que o usuário é autenticado e o token gerado, o STS efetua um POST para a aplicação solicitante. O conteúdo desta resposta é um HTML com um formulário (tag <form />), e dentro dele há vários campos ocultos, que representam os parâmetros do WS-Federation para a resposta (RSTR), e inclui também um pequeno código JavaScript para efetuar uma espécie de um auto-submit o formulário. Os parâmetros do WS-Federation utilizados aqui são:

  • wa: Contém a ação executada pelo STS.
  • wctx: Contém a URL original que o usuário tentou acessar na aplicação solicitante.
  • wresult: O resultado em si, que contém o token emitido pelo STS.

SSO - Single Sign-On

Como comentado acima, o WIF utiliza cookies no ambiente passivo para evitar que o usuário seja redirecionado a todo momento para o STS. Como sabemos, há uma regra em que uma aplicação (site) somente pode acessar os cookies que foram emitidos por ela, em nome daquele domínio em que ela se encontra. Mas dessa forma, como podemos garantir o Single Sign-On (SSO)? (SSO é a possibilidade de se autenticar uma única vez, em um local único, e assim poder acessar todas as aplicações que confiam neste autenticador, sem a necessidade de efetuar novamente o login)

Para que isso seja possível, o cookie pode ser gerado para o domínio da relying party ou para o domínio do STS. A segunda opção é a utilizada para garantir o SSO, já que o cookie é gerado em nível de STS e somente a partir daquele domínio é que poderemos acessá-lo. Uma vez que você possui várias aplicações confiando neste STS, os redirecionamentos são realizados até ele, mas ao detectar a presença de um cookie válido para aquele token, o formulário de login não será apresentando, redirecionando o usuário devolta para a aplicação solicitante, garantindo assim um ambiente confiável.

Conclusão: Neste extenso artigo, vimos como funciona os bastidores e como configuar o WIF em uma aplicação ASP.NET Web Forms. Como a Microsoft se preocupou em manter o mesmo modelo de programação quando falamos de autenticação e autorização, o simples fato de acoplarmos um dos módulos que vimos acima no pipeline do ASP.NET, já faz com que magicamente a identidade do usuário seja transformada em claims, e toda a complexidade de redirecionamentos entre as aplicações (relying parties e STSs) também já está embuitida nestes módulos. E como se não bastasse, ainda há o utilitário FedUtil.exe que nos auxilia na configuração, evitando conhecer o schema do WIF. Além disso, o WIF com ASP.NET facilita a implementação de SSO, tarefa que é extremamente árdua sem o uso dele.

Tags: , , ,

ASP.NET | Security | WIF

Auditando serviços WCF

by Israel Aece 3. March 2010 09:50

Como já comentei antes, o WCF fornece uma série de formas de autenticação e autorização, e mais recentemente, também já dá suporte ao WIF, assunto qual abordarei no futuro. Uma vez que a segurança esteja habilitada, uma necessidade que se tem é justamente como auditar os processos de autenticação que são realizados, independentemente do modelo de credencial que esteja sendo fornecido pelo cliente.

O WCF fornece vários pontos de estensibilidade, mas utilizá-lo há dois problemas, onde o primeiro é a necessidade de escrever o código necessário para isso, e o segundo, e mais complicado, é que a maioria (talvez todos) os pontos para acoplar algum código customizado, sempre acontece depois que o processo de autenticação já tenha sido efetuado. Já quando você possuir um modelo de autenticação customizado, você pode interceptar a validação do usuário, e caso ele não seja válido, você cataloga isso em algum lugar. Mas e quando o modelo de autenticação é Windows ou qualquer outra forma que o WCF já entende?

Apesar de informações bem simplistas, o WCF já traz nativamente um behavior que podemos utilizar em nível de serviço para auditar a autenticação. Como tudo no WCF, este behavior pode ser configurado de forma imperativa ou declarativa, e nos fornece quatro propriedades para a sua configuração, sendo elas:

  • AuditLogLocation: Todas as informações geradas pela auditoria são armazenadas no log do Windows. Esta opção permite especificar o local onde essas informações serão colocadas. Temos três possíveis opções, fornecidas pelo enumerador AuditLogLocation:
    • Default: Utiliza o log padrão do sistema operacional.
    • Application: Armazena as informações no event log Application.
    • Security: Armazena as informações no event log Security.
  • SuppressAuditFailure: Propriedade do tipo boleana, que permite especificar se eventuais falhas que aconteçam no momento da gravação do log sejam enviadas para a aplicação. Se a aplicação não se preocupa com as falhas que possam acontecer durante este momento, então é importante que se defina esta campo para True, caso contrário, as exceções comprometerão o funcionamento dela. Mas definir isso como True, faz com que possíveis exceções que seriam disparadas não sejam propagadas para a aplicação, e você nunca saberá se há algum problema, e quando realmente precisar recorrer aos logs de auditoria para saber se alguém acessou em um determinado dia e hora, as informações não estarão lá.
  • ServiceAuthorizationAuditLevel: Este nível de auditoria consiste em catalogar as informações de autorização que são realizadas em nível de serviço, utilizando a mesma política de segurança para todos os métodos.
  • MessageAuthenticationAuditLevel: Neste nível, a auditoria monitora os eventos que são gerados durante uma mensagem específica, que por sua vez, executará uma única operação.

As propriedades ServiceAuthorizationAuditLevel e MessageAuthenticationAuditLevel recebem uma das opções expostas pelo enumerador AuditLevel: None, Success, Failure ou SuccessOrFailure. A ideia aqui é permitir ao desenvolvedor catalogar somente aqueles eventos importantes para ele, pois talvez ele esteja somente interessado nas falhas que ocorreram. É importante dizer também que isso influencia no tamanho do log, e se você especificou um limite máximo, dependendo do volume de requisições que chegam para este serviço, em pouco tempo podemos exceder esse tamanho, e a partir daí, exceções podem começar a acontecer. Eis aqui um dos motivos para a existência da propriedade SuppressAuditFailure.

O código abaixo exibe a utilização deste behavior utilizando o modelo declarativo, através de um arquivo de configuração (App.config ou Web.config):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="Host.Servico" behaviorConfiguration="srvBehaviorConfig">
        <!-- endpoints -->
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="srvBehaviorConfig">
          <serviceSecurityAudit
            auditLogLocation="Application"
            messageAuthenticationAuditLevel="SuccessOrFailure"
            serviceAuthorizationAuditLevel="SuccessOrFailure"
            suppressAuditFailure="false" />
          <!-- configurações de segurança -->
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Abaixo temos o resultado que foi colocado no Event Log:

---------------------------------------------------------------------------------
Service authorization succeeded.
Service: http://localhost:8778/srv
Action: http://www.israelaece.com/IContrato/RecuperarDados
ClientIdentity: IsraelAece
AuthorizationContext: uuid-019ae4a4-2cb5-4414-9806-1a4e22a4bf79-2
ActivityId: <null>
ServiceAuthorizationManager: XmlAuthorizationManager
---------------------------------------------------------------------------------

Tags: , ,

WCF

ConfigurationName

by Israel Aece 25. September 2009 08:58

O WCF permite a configuração de um serviço de forma declarativa e imperativa, assim como já falei aqui. Quando optamos pela forma declarativa, todas as configurações do serviço/cliente serão feitas em um arquivo de configuração (App.config/Web.config), e com isso, sempre temos que referenciar o serviço e o contrato com o seu nome completo, ou seja, incluindo o namespace até eles.

Para facilitar isso, temos a propriedade ConfigurationName, exposta através dos atributos ServiceBehaviorAttribute e ServiceContractAttribute, onde podemos definir um "alias" para o serviço e o contrato, respectivamente. Com isso, evitamos especificar o nome completo até os tipos. O código abaixo ilustra a utilização dessa propriedade nos dois atributos:

[ServiceContract(ConfigurationName = "MeuContrato")]
public interface IContrato
{
    //...
}

[ServiceBehavior(ConfigurationName = "MeuServico")]
public class Servico : IContrato
{
    //...
}

E, se repararmos no arquivo de configuração agora, teremos:

<services>
  <service name="MeuServico">
    <host>
      <baseAddresses>
        <add baseAddress="http://localhost:9383"/>
      </baseAddresses>
    </host>
    <endpoint address="srv" binding="basicHttpBinding" contract="MeuContrato" />
  </service>
</services>

É importante dizer que isso não interfere no nome do serviço e do contrato que será exposto através do documento WSDL. Essas configurações são apenas características locais dos arquivos de configuração do serviço ou do cliente.

Tags:

WCF

Configuração do WCF no cliente

by Israel Aece 20. September 2009 16:01

Quando fazemos a referência a um serviço WCF em uma aplicação cliente, o arquivo de configuração sofre uma grande mudança, pois todas as configurações de acesso ao serviço (endpoints, bindings, behaviors, etc.), são colocadas dentro deste arquivo. Muitas vezes, o arquivo de configuração de uma aplicação já possui várias outras entradas, e colocar todas essas outras configurações, torna o arquivo muito extenso.

A partir do WCF 4.0, a Microsoft incluiu uma nova classe chamada ConfigurationChannelFactory<TChannel> (System.ServiceModel.Configuration), que herda diretamente da classe ChannelFactory<TChannel>. Com essa classe, podemos isolar as configurações do WCF em um arquivo de configuração a parte, dando uma maior flexibilidade, já que podemos criar um proxy (DLL) e distribuir o respectivo arquivo de configuração, e aquele que consume, não precisa fazer nada além de colocar o arquivo no diretório da aplicação.

Para trabalhar com essa classe, temos que recorrer a criação manual do proxy, assim como foi falado aqui. Primeiramente precisamos utilizar a classe ExeConfigurationFileMap, que recebe o nome de um arquivo de configuração, e que neste caso, conterá as configurações necessárias para o serviço. Em seguida, abrimos esse arquivo mapeado e o passamos para a classe ConfigurationChannelFactory<T>. Depois disso, tudo fica conforme já sabemos, invocando o método CreateChannel e, finalmente, o consumo das operações. O código abaixo ilustra esse exemplo:

ExeConfigurationFileMap map = new ExeConfigurationFileMap() { ExeConfigFileName = "wcf.config" };
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationLevel.None);
ConfigurationChannelFactory<IContrato> factory =
       new ConfigurationChannelFactory<IContrato>(
              "NetTcpBinding_IContrato",
              config,
              new EndpointAddress("net.tcp://localhost:9292"));
IContrato proxy = factory.CreateChannel();
Console.WriteLine(proxy.Ping("teste"));

Tags: ,

WCF

Habilitando o Performance Counter no WCF

by Israel Aece 16. September 2009 00:21

Como eu comentei aqui, o WCF fornece vários contadores de performance que você pode utilizar para monitorar os serviços. Há vários tipos de contadores, que mensuram pontos cruciais de toda a infraestrutura do WCF. A imagem abaixo ilustra o monitoramento das instâncias (das classes que representam o serviço) que são criadas, e a quantidade de chamadas por segundo.

Ao adicionar um contador, você pode escolher monitorar esses índices para todos os serviços que rodam naquela máquina ou, se desejar, poderá monitorar esse contador para um serviço específico. Para isso, ao adicionar um contador, selecione a instância correspondente, que é representada pelo endereço onde o serviço está exposto. Se por algum motivo, quiser ler essas informações a partir de uma aplicação customizada, então você deve recorrer ao uso da classe PerformanceCounter e PerformanceCounterCategory.

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