O Português e o Software

Artigos, preposições, pronomes, verbos, entre várias outras classes de palavras, são características de vários idiomas ao redor do mundo. Cada uma delas desempenha um papel importante, e ficaria difícil montarmos orações sem uma delas, pois coisas básicas como determinar e conectar elementos, expressar ações, etc., são por elas especificadas, e que impõem suas regras de construção.

Quando estamos criando um software, é importante que se nomeie os seus membros de forma à expressar exatamente como temos no mundo real. Isso facilita muito durante o levantamento, desenvolvimento e, principalmente, na manutenção do código, permitindo que pessoas que não conheçam exatamente os detalhes de uma linguagem de programação, olharem para o código e entenderem o que está acontecendo ali. Justamente por isso é importante que você tente manter – também – durante a criação de classes, métodos, propriedades e variáveis, os mesmos elementos gramaticais que são utilizados no mundo real.

Em um artigo anterior eu já havia comentado sobre a utilização do idioma português ao invés do inglês. Se você está criando um código de infraestrutura, como por exemplo, um repositório de logs, classes que facilitam o acesso ao registry do Windows, etc., então você pode manter isso em inglês, pois fica até mais fácil encontrar nomes mais coerentes. Já quando estamos construindo as regras de negócio, onde vamos utilizar algum modelo para o desenvolvimento (DDD, TS ou AR), devemos sim utilizar o português e nos atentarmos para manter as classes de palavras do idioma, para expressar com exatidão o negócio no mundo virtual.

Ao criar variáveis, eu considero o uso das preposições como sendo extremamente importante para conectar uma parte da variável à outra. Se vamos criar uma váriavel que irá armazenar o nome do cliente, ao invés de utilizarmos nomeCliente, é muito mais intuitivo chamarmos de nomeDoCliente. Acredito que nomear a variável como nomeCliente acaba sendo uma má interpretação do inglês, por exemplo, se fossêmos criar a mesma variável em inglês, nomearíamos para customerName, mas lembre-se de que a tradução para customer name é nome do cliente. Para elencar mais alguns exemplos práticos:

DateTime dataDeNascimento = DateTime.Now;
decimal valorTotalDoPedido = 1000.00M;
decimal? taxaDeJuros = null;
bool permiteContinuarCasoEncontreFalhasNoProcessamento = true;
Action<decimal> calculoDeSalario = (salario) => { };

O mesmo vale para métodos. O que temos em mente é que métodos devem ser representados por verbos. Isso continua sendo válido, mas você deve nomeá-lo exatamente de acordo com a sua finalidade. Geralmente você utiliza o verbo em primeiro lugar e o restante complementa de acordo com a tarefa que ele desempenha, por exemplo:

RepositorioDeClientes repositorioDeClientes = new RepositorioDeClientes();

Cliente cliente = new Cliente();
cliente.Nome = “Israel Aece”;
cliente.Salario = 1000.00M;

this.CalcularLimiteDaContaCorrente(cliente);
repositorioDeClientes.Adicionar(cliente);

Depois do limite calculado, eu estou adicionando o cliente recém criado no repositório. Note que o método Adicionar é nomeado como Adicionar e não como AdicionarCliente. Como eu já estou em um repositório de clientes, eu já sei que ali eu só posso adicionar clientes. Não precisa “ratificar” isso no método. Além de desnecessário, complica a leitura.

Outro detalhe importante é quando você vai criar sobrecargas (overloads) de métodos. Na maioria destes casos, são os parâmetros (tipo e quantidade) que determinam a diferença entre as várias versões e não o nome, pois os nomes serão sempre os mesmos. Por exemplo, você tem uma classe que efetua a simulação de empréstimos pessoais, onde é necessário informar o valor a ser financiado e o CPF do cliente. Só que o cliente pode não estar com o CPF, mas tem ele na CNH ou no RG. Então você pode criar versões do método Simular, onde cada um utiliza um documento específico.

public class SimuladorDeEmprestimosPessoais
{
    public void Simular(decimal valor, RG rg)
    {
        CPF cpf = ExtrairCPFDoRG(rg);
        Simular(valor, cpf);
    }

    public void Simular(decimal valor, CNH cnh)
    {
        CPF cpf = ExtrairCPFDaCNH(cnh);
        Simular(valor, cpf);
    }

    public void Simular(decimal valor, CPF cpf)
    {
        //efetua a simulação
    }
}

Acima vemos um exemplo prático e expressivo, que olhando para os seus métodos, somos capazes de efetuar a simulação dado qualquer um dos documentos suportados (RG, CNH ou CPF) pela classe.

Como eu disse acima, com esse tipo de código é possível que pessoas que nem conheçam detalhes do C#, olhem para ele e consigam extrair e entender grande parte das funcionalidades que ele executa. Essa técnica tornará o seu trabalho e dos demais membros da equipe muito mais simples, principalmente quando precisamos efetuar algum tipo de manutenção em um código já escrito há algum tempo. A ideia é tentar praticar o que Martin Fowler já falou há algum tempo: “Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”.

Role vs. Claims

Quando estamos lidando com a autorização dentro de uma aplicação, é muito comum configurarmos o acesso às seções dentro do mesmo de acordo com o papel (role) em que o usuário está contido. Muitas vezes o papel reflete o departamento ou grupo em que o usuário encontra-se cadastrado dentro da empresa, independentemente se esse controle é realizado através da própria aplicação ou através de algum centralizador, como é o caso do Active Directory (AD).

Mas ao trabalhar com autorização baseada em claims (afirmações), você não está restrito a isso. A autorização pode estar baseada em outras informações, que vão muito além de simples strings. Podemos, por exemplo, conceder o acesso somente aqueles usuários que são maiores de 18 anos, e para isso vamos nos basear na data de nascimento dele.

Ao criar uma claim, devemos especificar o seu tipo (String, Integer, DateTime, Double, etc) e o seu respectivo valor. Além disso, temos a espécie da claim, que podem ser valores predefinidos pelo WIF, e que determinará qual é a finalidade dela, como por exemplo: name, email, role, etc. Como podemos reparar, um papel nada mais é que uma claim, que é inserida dentro do token emitido pelo STS.

Para manter a compatibilidade com a estrutura de autorização do .NET, continuamos recorrendo à tradicional interface IPrincipal, ou melhor, à uma de suas derivações, que é a IClaimsPrincipal. Ao interrogar o método IsInRole, que dado um papel (role), retornará um valor boleano indicando se o usuário está ou não contido nele. Quando você utiliza esse método, o WIF procura dentro da coleção de claims emitidas pelo STS, alguma que corresponda ao papel. Por padrão, ele tenta procurar pela claim com o nome de “role”, mas você pode alterar esse comportamento. Se por algum motivo, a claim que representa o papel é uma outra claim com um outro nome, você pode alterar isso através da propriedade RoleClaimType da interface IClaimsIdentity.

Já se você precisar refinar a sua autorização através de alguma outra claim que não seja o papel do usuário, você pode recorrer a coleção de claims, que também é exposta através da propriedade Claims da interface IClaimsIdentity. Por exemplo, se o STS emitir uma claim que corresponde à data de nascimento do usuário, e quisermos somente conceder o acesso se ele for maior que 18 anos, então podemos utilizar o seguinte código:

IClaimsIdentity identity = Thread.CurrentPrincipal.Identity as IClaimsIdentity;

if (identity != null)
{
    string data =
        (
            fromin claimsIdentity.Claims
            where c.ClaimType == ClaimTypes.DateOfBirth
            select c.Value
        ).SingleOrDefault();

    if (!string.IsNullOrWhiteSpace(data))
    {
        const int IDADE_PERMITIDA = 18;
        DateTime dataDeNascimento = DateTime.MinValue;

        if (DateTime.TryParse(data, out dataDeNascimento))
        {
            this.EntrarNoSite.Enabled =
                (DateTime.Now.Subtract(dataDeNascimento).Days / 365) > IDADE_PERMITIDA;
        }
    }
}

Conclusão: O STS pode emitir claims que descrevem o usuário que ele conhece, sendo essas claims informações que a aplicação pode utilizar como quiser para poder identificar ou refinar a autorização daquele usuário, e como disse anteriormente, nem sempre utilizaremos roles para customizar o acesso. Finalmente, podemos concluir que todo papel (role) é uma afirmação (claim), mas nem toda afirmação é um papel.

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

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.

Autenticação via Claims no ASP.NET MVC

Anteriormente eu comentei como podemos utilizar o WIF em uma aplicação ASP.NET WebForms, analisando detalhadamente os passos necessários para efetuar a configuração dos módulos utilizados pelo WIF. Mas o WIF também pode ser utilizado em conjunto com aplicações ASP.NET MVC. A finalidade deste artigo é mostrar algumas dicas importantes para fazer com que o WIF seja acoplado ao pipeline de uma relying party construída com o ASP.NET MVC, pois ao contrário do que vimos no artigo anterior, não há templates de projetos com o WIF pré-configurado para o ASP.NET MVC.

O primeiro passo é importante para ser dito, é que para utilizar o WIF com o MVC, também devemos recorrer aos módulos WSFederationAuthenticationModule e SessionAuthenticationModule, que são acoplados ao pipeline do ASP.NET e são os responsáveis pelo processamento e gerenciamento do processo de autenticação do usuário. Sendo assim, é necessário adicioná-los manualmente na seção <httpModules /> do arquivo Web.config, ou se estiver utilizando o assistente Federation Utility, que está disponível a partir da IDE do Visual Studio, ele já fará essa configuração automaticamente.

Como sabemos, a infraestrutura de segurança do o ASP.NET MVC é igual ao do ASP.NET WebForms, e com isso devemos respeitar as mesmas imposições feitas quando utilizamos o WIF no WebForms, ou seja, configurar o modelo de autenticação como indefinido (None), pois essa tarefa não compete mais à esta aplicação. Além disso, temos também que criar e configurar adequadamente a seção <microsoft.identityModel />, que como já sabemos, isso acaba sendo realizado automaticamente quando utilizamos o Federation Utility.

O primeiro passo é você se preocupar em proteger as seções da aplicação MVC que não permitem o acesso anônimo. Ao contrário do WebForms, no MVC definimos quais são as actions (métodos) que são restritas. Para definir isso, podemos utilizar o arquivo Web.config ou através do atributo AuthorizeAttribute. O código abaixo negará o acesso anônimo aos métodos (actions) VisualizarItens e RecuperarIndice da classe (controller) Monitor. Para maiores detalhes, você pode consultar este artigo.

[Authorize]
public class MonitorController : Controller
{
    public ActionResult VisualizarItens()
    {
        //…
    }

    public ActionResult RecuperarIndice()
    {
        //…
    }
}

Pelo fato de termos os módulos do WIF acoplados ao MVC, ao detectar que o usuário não tem permissão de acesso a um destes métodos, automaticamente o módulo WSFederationAuthenticationModule entrará em ação e redirecionará o usuário para o STS que ele confia, incluindo na requisição a action e o controller que ele tentou acessar, para que quando o STS validá-lo, o WIF consiga redirecionar o usuário para o mesmo local.

Outro detalhe importante quando configuramos o WIF no ASP.NET MVC, é o uso do atributo passiveRedirectEnabled do elemento wsFederation. Quando ele é definido como False, o desenvolvedor da relying party deverá explicitamente efetuar o redirecionamento para o STS quando necessário. No ASP.NET WebForms, podemos fazer uso do controle FederatedPassiveSignIn, que tem a finalidade de encapsular toda a complexidade deste processo. Mas como sabemos, no MVC não utilizamos esses tipos de controles, o que nos obriga a fazer o redirecionamento manual, lembrando que isso somente é necessário quando o atributo passiveRedirectEnabled for definido como False.

Quando necessitamos desligar o auto-redirecionamento, precisamos saber como proceder para conseguir interagir com o STS. Para exemplificar isso, temos que criar um controller que irá gerenciar o processo de login, logout e o processamento do token gerado. Esse controller utilizará alguns dos membros que são expostos pela API do WIF, que vimos em um artigo anterior. O controller irá definir actions que determina cada uma das tarefas que ele vimos acima, podendo criar nas views, links que direcionam para cada uma delas.

public class SecurityController : Controller
{
    public ActionResult EfetuarLogin()
    {
        if (!User.Identity.IsAuthenticated)
        {
            var fam = FederatedAuthentication.WSFederationAuthenticationModule;
            var requestMessage = new SignInRequestMessage(new Uri(fam.Issuer), fam.Realm)
            {
                Context = “EndereçoParaOndeUsuarioSeraDirecionadoDepoisDoLogin”
            };

            return new RedirectResult(requestMessage.WriteQueryString());
        }

        return this.View();
    }

    [ValidateInput(false)]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult ProcessarToken()
    {
        var fam = FederatedAuthentication.WSFederationAuthenticationModule;

        if (fam.CanReadSignInResponse(HttpContext.Current.Request, true))
        {
            string url = HttpContext.Current.Request.Form[“wctx”];
            return new RedirectResult(url);
        }

        return this.View();
    }

    public ActionResult EfetuarLogout()
    {
        if (User.Identity.IsAuthenticated)
        {
            var fam = FederatedAuthentication.WSFederationAuthenticationModule;
            var signOutMessage = new SignOutRequestMessage(new Uri(fam.Issuer));

            fam.SignOut(false);
            return new RedirectResult(signOutMessage.WriteQueryString());
        }

        return this.View();
    }
}

O primeiro método, chamado de EfetuarLogin, extrai alguns parâmetros do módulo WSFederationAuthenticationModule e cria uma mensagem de login para o STS. A mensagem de login é representada pela instância da classe SignInRequestMessage, que fornece uma propriedade chamada Context, que recebe uma URL para qual o usuário será direcionado depois do login ter sido realizado. Finalmente, utilizando o método WriteQueryString, que retorna uma string representando o endereço completo até o STS, incluindo os parâmetros necessários para que o STS faça toda a validação necessária, e passamos essa string através de um objeto RedirectResult, que faz com que o MVC redirecione o usuário ao STS.

Na sequência temos o método ProcessarToken, que é responsável por verificar se a requisição trata-se de um token gerado pelo STS, e isso é realizado através do método CanReadSignInResponse da classe WSFederationAuthenticationModule. Se este método retornar True, então extraímos o parâmetro wctx da requisição e redirecionamos o usuário para este endereço. O wctx representa o endereço que o usuário tentou acessar antes de efetuar o login no STS, qual utilizaremos o RedirectResult para direcionar o usuário novamente para lá. Podemos notar que este método está decorado com dois atributos, onde o primeiro deles desligamos a validação para permitir que informações com caracteres < e > sejam postadas para ele. Isso é necessário porque o token gerado é baseado em XML. Em seguida, utilizamos o atributo AcceptVerbsAttribute para garantir que este método será acessado somente através do verbo POST.

Finalmente, temos o método EfetuarLogout, que coordena o processo de logout do usuário. Ele utiliza o método SignOut da classe WSFederationAuthenticationModule, e na sequência utilizamos uma instância da classe SignOutRequestMessage, que recebe em seu construtor o endereço do STS. Depois da instância criada, utilizamos novamente um objeto RedirectResult para redirecionar o usuário para o endereço gerado pelo método WriteQueryString da classe SignOutRequestMessage, que efetuará o logout no STS.

Depois deste controller criado, tudo o que precisamos fazer na aplicação, é mencionar as actions login e logout onde desejamos visualizar estes links. Para exemplificar, o código abaixo ilustra como criar estes links:

<%= Html.ActionLink(“Login”, “EfetuarLogin”, “Security”) %>
<%= Html.ActionLink(“Logout”, “EfetuarLogout”, “Security”) %>

Conclusão: Como percebemos neste artigo, apesar de não termos toda a facilidade de configuração e controles que existem no ASP.NET WebForms, é fácil acoplar o WIF à execução de uma aplicação ASP.NET MVC. A única exigência aqui é você tentar identificar se precisa ou não do auto-redirecionamento; caso não precisar, o controller que foi criado acima não será necessário.

Community Launch em Piracicaba

No próximo dia 17 de abril acontecerá um evento chamado de Community Launch. Esse evento será espalhado por todo o Brasil, onde cada região criará o evento e abordará os principais produtos da Microsoft que estarão sendo lançados neste ano de 2010, a saber: Visual Studio 2010, Windows Server 2008 R2 e SQL Server 2008 R2.

Para quem é de Piracicaba e região, este evento acontecerá na Faculdade Salesiana Dom Bosco, à partir das 08:30 da manhã. Para maiores detalhes, consulte o site do evento. Entre as várias palestras, eu sou o responsável por falar sobre o WCF – Windows Communication Foundation. É importante dizer que a palestra será de nível 100, abordando superficialmente algumas das principais funcionalidades e mudanças que aconteceram nesta nova versão da tecnologia. Abaixo temos a descrição da palestra:

Título: Overview do WCF
Palestrante: Israel Aece
Descritivo: O WCF é o pilar de comunicação da plataforma .NET, e que está presente desde a versão 3.0. Através dele podemos construir serviços que podem ser consumidos pela própria empresa ou por parceiros de negócios, para que seja possível criar aplicações inteligentes e conectadas. A finalidade desta palestra é mostrar um overview sobre o WCF, abordando superficialmente as funcionalidades incluídas na versão 4.0, com exemplos práticos de sua utilização.

IMPORTANTE: Apesar do evento ser gratuito, para poder participar é necessário efetuar o cadastro aqui.

Expect100Continue em serviços WCF

Por padrão, todas as requisições HTTP que são relizadas pelas classes que estão contidas no namespace System.Net, adicionam um header chamado “Expect: 100-continue”. A finalidade deste header, criado a partir da versão 1.1 do HTTP, quando é implementado corretamente pelos servidores Web, servem para que o servidor, baseando-se apenas nos headers da requisição, determine se a mensagem será ou não aceitada por ele, sem a necessidade de enviar toda a mensagem para depois rejeitá-la.

Quando você tenta consumir um serviço WCF exposto através do protocolo HTTP, eventualmente poderá receber a seguinte mensagem de erro: The remote server returned an unexpected response: (417) Expectation failed. Provavelmente isso pode ser pela má implementação deste recurso do protocolo, que evita conseguirmos consumir o serviço. Para resolver este problema precisamos desativar o envio deste header, que acaba provocando este comportamento. Para isso, podemos recorrer à propriedade estática Expect100Continue da classe ServicePointManager, que recebe um valor boleano (que por padrão é True), indicando se este recurso está ou não habilitado. No nosso caso, devemos definí-la como False antes da chamada da operação do serviço:

System.Net.ServicePointManager.Expect100Continue = false;

Ou, para ficar mais flexível, utilizando o arquivo de configuração:

<system.net>
  <settings>
    <servicePointManager expect100Continue=”false”/>
  </settings>
</system.net>

Novas funcionalidades do Process Explorer

A nova versão do Process Explorer, ferramenta que utilizamos para gerenciar e monitorar processos dentro do sistema operacional, passa a ter novas funcionalidades para processos gerenciados, ou melhor, para conseguirmos visualizar algumas informações relacionados exclusivamente ao .NET (CLR). A partir de agora ele é capaz de listar dos AppDomains que existem dentro da aplicação, os assemblies que foram carregados para dentro de cada um deles, contadores de performance relacionados à exceções, threading, etc. Essas informações já estavam acessíveis a partir de contadores de performance do Windows (perfmon.exe), mas agora fica muito mais fácil e intuitivo de chegar até elas. A imagem abaixo ilustra as suas duas novas abas:

Explorando os módulos do WIF para o ASP.NET

Como vimos no artigo anterior, o WIF fornece vários módulos para acoplarmos ao pipeline do ASP.NET, e que farão tudo o que for necessário para efetuar a comunicação com o respectivo STS. Esses módulos são aqueles tradicionais módulos do ASP.NET, que implementam a interface IHttpModule, e se vinculam à eventos que “cortam” a requisição na vertical.

Esses módulos são: WSFederationAuthenticationModule e SessionAuthenticationModule, e estão debaixo do namespace Microsoft.IdentityModel.Web. O primeiro é responsável pelo processamento do token que é gerado pelo STS, enquanto o segundo persiste essa informação em um cookie, e nas requisições subsequentes, ele irá analisá-lo, e caso não exista ou esteja inválido, redirecionará o usuário novamente para o STS.

Ambas as classes (módulos), fornecem uma série de membros que permite aos desenvolvedores ASP.NET a interceptarem alguns estágios do processamento através de eventos, adicionando um código customizado seja ele para manipulação ou auditoria. Além disso, esses módulos também oferecem alguns métodos e propriedades para permitir a reutilização de algumas funcionalidades/informações. A finalidade deste artigo é explorar os principais membros públicos e o que e como podemos fazer para efetuar tal customização.

Antes de efetivamente falarmos dos membros que são disponibilizados, precisamos saber como temos acesso à cada um deles. Como é responsabilidade do próprio ASP.NET criar os módulos que disponibilizam esses eventos, de algum forma precisamos acessar essas instâncias criadas. Para isso, existe uma classe estática chamada FederatedAuthentication, que também está debaixo do namespace Microsoft.IdentityModel.Web. Essa classe fornece apenas cinco propriedades, também estáticas, a saber:

  • ClaimsAuthorizationModule: Retorna a instância do módulo ClaimsAuthorizationModule. Este módulo é acoplado ao runtime do ASP.NET no evento OnAuthorizeRequest, invocando o método CheckAccess da classe ClaimsAuthorizationManager, que você pode utilizar para customizar a autorização.
  • ClaimsPrincipalHttpModule: Retorna a instância do módulo ClaimsPrincipalHttpModule, que faz uma espécie de tradução, tranformando o principal e identity corrente, em objetos que implementam as interfaces IClaimsIdentity e IClaimsPrincipal.
  • ServiceConfiguration: Essa propriedade retorna uma instância da classe ServiceConfiguration, que representa a seção do arquivo de configuração que corresponde ao WIF.
  • SessionAuthenticationModule: Retorna a instância do módulo SessionAuthenticationModule. Sua funcionalidade já foi discutida acima e neste outro artigo.
  • WSFederationAuthenticationModule: Retorna a instância do módulo WSFederationAuthenticationModule. Sua funcionalidade também já foi discutida acima e neste outro artigo.

A classe FederatedAuthentication ainda fornece um evento ServiceConfigurationCreated, que é disparado depois que a seção do WIF foi totalmente carregada. Agora, depois de conhecermos como podemos chegar até os eventos, podemos recorrer ao modo tradicional do .NET para nos vincularmos à eles, que é através do operador +=. Mas temos também tem uma segunda alternativa, onde podemos definir o tratador do evento utilizando o nome do módulo (classe) e o nome do evento, diretamente dentro do arquivo Global.asax, assim como podemos ver no exemplo abaixo:

<%@ Import Namespace=”Microsoft.IdentityModel.Configuration” %>
<%@ Import Namespace=”Microsoft.IdentityModel.Web” %>

void WSFederationAuthenticationModule_SignedIn(object sender, EventArgs e)
{
    //…
}

WSFederationAuthenticationModule – Propriedades

Este módulo fornece várias propriedades que ajudam na configuração do redirecionamento para o STS, na geração da mensagem de requisição e no processamento do token emitido pelo STS. Grande parte dessas propriedades acabam sendo configuradas através da seção do WIF (<microsoft.identityModel />) que encontra-se dentro do arquivo Web.config da aplicação que corresponde à relying party.

Entre essas propriedades, temos a PassiveRedirectEnabled. Essa propriedade recebe um valor boleano, que indica ao WIF que ao detectar que o usuário não está autenticado, irá redirecioná-lo automaticamente para o STS configurado na aplicação. É importante dizer que se utilizarmos o controle FederatedPassiveSignIn, essa propriedade deverá estar definida como False, assim como foi comentado neste outro artigo.

Issuer também é uma propriedade importante, que recebe o endereço do STS, para qual o usuário será redirecionado para efetuar a autenticação. A propriedade Reply contém o endereço da relying party, para que o STS possa identificar se ele pode ou não emitir o token para ela.

WSFederationAuthenticationModule – Métodos

Essa classe ainda fornece vários métodos, mas que na maioria das vezes acabam sendo utilizados pelo próprio runtime. O primeiro deles é o método CanReadSignInResponse, que recebe como parâmetro uma requisição (HttpRequest) e retorna um valor boleano indicando se ela se refere à uma mensagem de autenticação, gerada pelo STS. CreateSignInRequest é um método que dado alguns parâmetros, retorna uma instância da classe SignInRequestMessage, que corresponde à mensagem de solicitação de login ao STS.

Ainda temos um método estático, chamado de FederatedSignOut. Esse método tem a finalidade de efetuar a requisição ao STS para efetivar o logout. Já o método SignOut apenas notifica a relying party de que o usuário efetuou o logout. Quando você invoca o primeiro deles, FederatedSignOut, depois de efetuado o logout no STS, o usuário é redirecionado novamente para a relying party, e quando o WIF dectecta que ele fez o logout no STS, invoca o método SignOut, disparando assim os eventos correspondentes para que a relying party seja notificada de que isso realmente aconteceu.

Já o método IsSignInResponse, dado a requisição (HttpRequest), retorna um valor boleano indicando se a requisição trata-se da resposta dada pelo STS. Você pode utilizá-la para burlar ou evitar algum processamento que não deveria acontecer nesta situação. Temos também o método RedirectToIdentityProvider, que como o próprio nome diz, me permite, de forma explícita, redirecionar o usuário para o STS. Isso te dá uma flexibilidade quando você não quer o redirecionamento automático que o WIF faz ao identificar que o usuário não está autenticado.

WSFederationAuthenticationModule – Eventos

O primeiro evento que temos dentro deste principal módulo do WIF é o RedirectingToIdentityProvider. Este evento é disparado antes do usuário ser redirecionado para o STS, dando a oportunidade de customizar o redirecionamento, a mensagem de requisição que é enviada (RST) e a oportunidade de cancelá-la. Tudo isso é fornecido através do argumento RedirectingToIdentityProviderEventArgs, que possui apenas duas propriedades: Cancel e SignInRequestMessage.

Depois deste evento temos os eventos que são disparados durante o recebimento e processamento do token gerado pelo STS, que são: SecurityTokenReceived, SecurityTokenValidated e SessionSecurityTokenCreated. O primeiro é disparado quando a resposta com o token foi enviada pelo STS. Já o segundo, é disparado depois que o token foi validado pelo WIF e o objeto IClaimsPrincipal já foi criado com as claims devolvidas pelo STS, e que podemos ter acesso através do argumento SecurityTokenValidatedEventArgs que é passado para este evento. Finalmente, o evento SessionSecurityTokenCreated, que tem a finalidade de notificar que o token foi recebido, validado e criado, e já está pronto para emitir o cookie para armazená-lo, mas isso depende do valor que é colocado na propriedade WriteSessionCookie, fornecido pelo argumento SessionSecurityTokenCreatedEventArgs. Depois desse estágio, quem faz as manipulações no cookie é o módulo SessionAuthenticationModule, que veremos mais adiante.

Há também eventos que são disparados durante o processo de login, a saber: SignedIn e SignInError. O primeiro evento é disparado depois que o objeto IClaimsPrincipal já foi definido para a thread/requisição corrente, e com isso, seguramente você poderá recorrer as propriedades HttpContext.Current.User e Thread.CurrentPrincipal. O segundo evento, SignInError, é disparado quando algum erro ocorre dentro deste módulo durante a recepção ou validação do token, e você pode utilizar esse evento para verificar a exceção que foi disparada, ter oportunidade de conseguir tratá-la e, eventualmente, catalogar em algum lugar.

Esse módulo ainda fornece mais três eventos que correspondem ao processo de logout: SigningOut, SignedOut e SignOutError. O primeiro é disparado antes do processo de logout iniciar, e segundo ocorre quando o processo de logout foi concluído e, finalmente, o evento SignOutError é disparado se algum problema acontecer durante o logout, e justamente por isso, ele inclui uma propriedade chamada Exception, que retorna a instância da exceção disparada.

E, finalmente, o evento AuthorizationFailed é disparado sempre quando você tenta acessar algum recurso protegido e ainda não está autenticado. Esse evento fornece como argumento um objeto do tipo AuthorizationFailedEventArgs, que por sua vez, disponibiliza uma propriedade chamada RedirectToIdentityProvider, que é do tipo boleana, indicando se o WIF deve ou não redirecionar o usuário para o STS.

SessionAuthenticationModule – Propriedades

A principal propriedade exposta por essa classe é a CookieHandler. Essa propriedade, que expõe um objeto com o mesmo nome, é responsável por armazenar o token gerado pelo STS em um cookie do lado do cliente. Além de gerar o cookie, o módulo SessionAuthenticationModule verifica a existência do mesmo, e caso exista, evita o redirecionamento para o STS em futuras requisições. O cookie handler também é estensível, e veremos mais detalhes sobre isso em um artigo específico.

SessionAuthenticationModule – Métodos

Como essa classe gerencia o cookie que corresponde ao token gerado pelo STS, ela também fornece métodos auxiliares que permitem ter acesso ao cookie em questão. O método ContainsSessionTokenCookie recebe uma coleção de cookies (que na maioria das vezes está dentro do objeto HttpRequest), e retorna um valor boleano indicando se o cookie existe dentro dela. Já o método TryReadSessionTokenFromCookie recebe um parâmetro de saída do tipo SessionSecurityToken, e retorna um valor boleano indicando se foi posssível ler o cookie. Caso retorne True, você pode seguramente acessar o parâmetro de saída, que corresponderá ao token gerado pelo STS.

O método DeleteSessionTokenCookie apenas tem a finalidade de excluir o cookie que corresponde ao token daquele usuário atual. Apesar de público, ele geralmente é invocado durante o processo de logout.

SessionAuthenticationModule – Eventos

Para evitar que o usuário seja redirecionado para o STS a toda requisição que fazemos à alguma página aplicação, esse módulo é adicionado ao runtime do ASP.NET com a finalidade de evitar essa transição a todo momento. Ele verifica se a requisição possui o cookie que corresponde ao token do usuário, que este módulo gerou quando ele (o usuário) se autenticou no STS. Ao detectar a presença deste cookie, ele extrai a informação e permite, através de dois eventos, interceptar o trabalho que ele faz. Esses eventos são: SessionSecurityTokenReceived e SessionSecurityTokenCreated.

Como disse acima, durante a execução este módulo verifica se o cookie está presente, e se estiver, recria o token a partir dele, e após isso, o evento SessionSecurityTokenReceived é disparado. Esse evento fornece como argumento um objeto do tipo SessionSecurityTokenReceivedEventArgs, qual você pode utilizar para re-emitir o cookie, permitindo você a manipular várias características dele, como por exemplo o tempo de expiração do token.

Já o segundo evento, SessionSecurityTokenCreated, é disparado durante a re-emissão do cookie, que muitas vezes acontece quando ele expira. Como argumento, este evento fornece um objeto do tipo SessionSecurityTokenCreatedEventArgs, que fornece duas propriedades: SessionToken e WriteSessionToken. A primeira corresponde ao token do usuário que você pode customizar aqui; já a segunda, recebe um valor boleano indicando se o cookie que irá ser regerado deve ou não ser persistido.

Esse módulo ainda fornece mais três eventos que correspondem ao processo de logout: SigningOut, SignedOut e SignOutError. O primeiro é disparado antes do processo de logout iniciar, o segundo ocorre quando o processo de logout for concluído e, finalmente, o evento SignOutError que é disparado se algum problema acontecer durante o logout, e justamente por isso, ele inclui uma propriedade chamada Exception, que retorna a instância da exceção disparada.

Conclusão: Este artigo mostrou os principais membros dos módulos do WIF, e que você pode fazer uso deles quando o WIF estiver habilitado na aplicação. Como notamos na descrição de cada um dos eventos disponibilizados pelos módulos, eles servem para interceptar vários momentos durante o redirecionamento, validação e persistência do token, e você pode ou não optar por utilizá-los, e isso dependerá da sua necessidade.

Compressão em Serviços WCF

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&#8221;
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.

Community Launch em Campinas

No próximo dia 20 de março acontecerá um evento chamado de Community Launch. Esse evento será espalhado por todo o Brasil, onde cada região criará o evento e abordará os principais produtos da Microsoft que estarão sendo lançados neste ano de 2010, a saber: Visual Studio 2010, Windows Server 2008 R2SQL Server 2008 R2.

Para quem é de Campinas e região, este evento acontecerá na Universidade UNIP, localizada no bairro Swift, à partir das 09:00 da manhã. Caso você não saiba exatamente onde ela está localizada, consulte este endereço para saber como chegar até lá. Entre as várias palestras, eu sou o responsável por falar sobre Visual Studio 2010 e .NET Framework 4.0. É importante dizer que a palestra será de nível 100, que além de abordar superficialmente algumas das principais funcionalidades desta nova versão, falaremos um pouco também sobre algumas tecnologias que já existem e estão à disposição desde as versões anteriores. Abaixo temos a descrição da palestra:

Título: Visual Studio 2010 e .NET Framework 4.0
Palestrante: Israel Aece
Descritivo: Esta palestra tem a finalidade de exibir um “tour” sobre a plataforma .NET, e a principal ferramenta que utilizamos para desenvolver aplicações para ela, que é o Visual Studio. A ideia é demonstrar rapidamente a história e a evolução do .NET Framework até a versão 4.0, incluindo assuntos como as linguagens de programação (C# e VB.NET), acesso à dados e aplicações para internet (WebForms, MVC e Silverlight). Logo após, vamos analisar de forma superficial as novidades que estão sendo incluídas na IDE do Visual Studio 2010 e que dão suporte ao desenvolvimento de qualquer tipo de aplicação sobre a plataforma .NET.

IMPORTANTE: Apesar do evento ser gratuito, para poder participar é necessário efetuar o cadastro aqui.