Web Commands

Quando falamos de CQRS, temos as consultas e os comandos. Esses comandos podem ser consumidos diretamente por aplicações, mas como expor publicamente para os clientes? Uma opção comum nos dias de hoje é utilizar o protocolo HTTP, facilitando assim o consumo pelas mais diversas linguagens e tecnologias. E quais as opções no .NET para fazermos esta exposição?

O ASP.NET é um candidato para isso, já que em sua versão core está modularizado e flexível, e assim podemos otimizar o pipeline de execução com somente aquilo que seja de fato necessário para executar os comandos. A finalidade do vídeo abaixo é exibir uma forma alternativa para incorporar os comandos do domínio no ASP.NET, utilizando o mínimo de recursos possível do hosting. Link para o código do exemplo.

Interface IApplicationLifetime

Desde o ASP clássico nós temos um arquivo chamado Global.asa. Com o ASP.NET, este arquivo foi renomeado para Global.asax. A finalidade deste arquivo é fornecer uma forma de interceptar eventos que ocorrem no nível da aplicação. Entre estes eventos, nós temos um que é disparado indicando que a aplicação foi inicializada, outro que algum erro não tratado aconteceu, que uma nova requisição chegou, que a aplicação está sendo encerrada, etc.

Este arquivo já não existe mais no ASP.NET Core, porém a necessidade ainda existe em alguns tipos de aplicação. Para interceptar a inicialização da aplicação e o término dela, podemos recorrer à interface IApplicationLifetime. Esta interface fornece três propriedades que indica a inicialização da aplicação (ApplicationStarted), que ela está sendo encerrada (ApplicationStopping) e depois de encerrada (ApplicationStopped). Essas propriedades são do tipo CancellationToken, e através de um delegate do tipo Action, passamos para o método Register o código customizado que queremos executar quando estes eventos acontecerem.

Para exemplificar o uso e utilidade desta interface, considere o exemplo abaixo onde temos uma classe estática que gerencia o agendamento e execução de tarefas pelo Quartz.NET. Além do método Schedule, temos os métodos Start e Stop, que como já podemos perceber, queremos invocar cada um deles no início e no fim da aplicação.

public class TaskManager
{
    public static void Start()
    {
        Scheduler = StdSchedulerFactory.GetDefaultScheduler().Result;
        Scheduler.Start();
    }

    private static IScheduler Scheduler { get; set; }

    public async static Task Schedule<TJob>(IDictionary data) where TJob : IJob
    {
        await Scheduler.ScheduleJob(
            JobBuilder
                .Create<TJob>()
                .SetJobData(new JobDataMap(data))
                .Build(),
            TriggerBuilder
                .Create()
                .WithSimpleSchedule(x => x.WithRepeatCount(0))
                .Build());
    }

    public static void Shutdown()
    {
        Scheduler?.Shutdown(true);
    }
}

O método Start deverá ser chamado quando a aplicação estiver sendo inicializada. Já no momento, do encerramento, recorremos ao método Shutdown do Schedule, passando o parâmetro true, que indica que antes de encerrar o gerenciador de tarefas, é necessário aguardar o término de alguma que, eventualmente, ainda esteja em execução. A interface IApplicationLifetime, assim como diversas outras do ASP.NET Core, o próprio runtime cria a instância de uma implementação padrão e passa para o método Configure da nossa classe Startup. Com isso, basta associarmos os métodos aos respectivos eventos, conforme é possível visualizar abaixo:

public void Configure(
    IApplicationBuilder app, 
    IHostingEnvironment env, 
    ILoggerFactory log, 
    IApplicationLifetime lifetime)
{
    lifetime.ApplicationStarted.Register(() => TaskManager.Start());
    lifetime.ApplicationStopped.Register(() => TaskManager.Shutdown());

    //outras configurações
}

Por fim, a aplicação (os controllers) interagem com a classe TaskManager a partir do método Schedule<TJob>, indicando qual a tarefa que precisa ser executada e parâmetros contextuais que queremos passar para a implementação da mesma.

[HttpPost]
public async Task<IActionResult> AgendarExecucao()
{
    await TaskManager
        .Schedule<GeracaoDeDados>(
            Request.Form.ToDictionary(x => x.Key, x => x.Value.ToString()));

    return View("ExecucaoAgendada");
}

Segregando Requisições no ASP.NET

No ASP.NET Core temos o conceito de middlewares, que nada mais são que plugins que podemos acoplar à execução para que eles sejam executados a medida em que uma nova requisição chega para o serviço ou para o site. Além de vários que já foram criados pela Microsoft, a criação destes middlewares é extremamente simples, e é a principal forma que temos de interceptar o momento antes e/ou depois das requisições.

Durante a inicialização da aplicação, devemos realizar as configurações necessárias para que ela funcione, e é neste momento que montamos o pipeline por onde cada uma delas deverá passar, e isso quer dizer que faremos uso constante de middlewares, mesmo que não se esteja explicitamente claro no código que vamos escrever. A execução ocorre de forma linear, onde a requisição é entregue para o primeiro middleware e este é responsável por fazer alguma atividade e passar adiante a requisição (leia-se para o middleware interno). Abaixo temos um exemplo de um simples middleware; note que temos algumas convenções que precisam ser respeitadas, tais como: receber no construtor o próximo middleware, ter o método chamado Invoke que será disparado pelo ASP.NET no momento adequado. É dentro deste método que você define quando quer invocar o próximo middleware.

public class LoggingMiddleware
{
    private readonly RequestDelegate next;

    public LoggingMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        //Ação Antes
        await next(context);
        //Ação Depois
    }
}

O problema aqui é que podemos hospedar na mesma aplicação dois conteúdos que demandam diferentes rotinas e características. É comum encontrarmos aplicações que expõe uma API web, e na mesma aplicação, fornece o acesso à um pequeno site com a documentação, tutoriais, central de suporte para a mesma. Do lado da API, é comum termos log de acesso, telemetria, controle de transações, etc., enquanto do “lado do UI” da aplicação, necessitamos de outros recursos, como a exposição de conteúdos estáticos e geração de HTML.

O ASP.NET possibilita uma forma de segregar as requisições (branch), roteando cada uma delas de acordo com um critério e, que se atendido, podemos customizar um pipeline de middlewares exclusivo para aquele cenário. Isso evita ter um único pipeline com todos os passos e ter que dar by-pass de acordo com a requisição que estamos processando naquele momento exige. Para isso, podemos recorrer ao método Map da interface IApplicationBuilder. Em sua versão mais simplista, ele aceita uma string que permite especificarmos a URL que, se atendida, deverá ser executada utilizando a configuração que especificamos no segundo parâmetro deste mesmo método. Há uma variação deste método chamado MapWhen, que nos permite inspecionar outros itens da requisição (headers, por exemplo) para tomar a decisão se deve ou não ser processada por aquele pipeline.

No exemplo abaixo, se a requisição conter /commands na URL, então o middleware de logging será disparado, na sequência o de transações, e por fim, a ação do controller será executada. Já quando a requisição for para /help, estamos permitindo o acesso à arquivos estáticos (imagens, PDFs, etc.) e também o uso do MVC para renderizar HTML com o conteúdo da documentação da API:

public void Configure(IApplicationBuilder app)
{
    app.Map("/commands",
        config =>
            config
                .UseMiddleware<LoggingMiddleware>()
                .UseMiddleware<TransactionMiddleware>()
                .UseMvc());

    app.Map("/help", 
        config => 
            config
                .UseMvc()
                .UseStaticFiles());
}

Se desejar ter acesso ao código completo deste artigo, consulte este endereço.

Rescrevendo Controllers

Codificar as ações (métodos) dentro de um controller não é uma tarefa difícil. A implementação varia muito, e todas elas podem ser consideradas corretas se atingirem o objetivo esperado. A finalidade deste vídeo é apresentar uma forma alternativa para a escrita de controllers e ações, mantendo o mesmo comportamento, porém organizando, estruturando e reaproveitando melhor os códigos que serão executados.

A ideia é mostrar uma outra forma para escrever controllers do ASP.NET MVC ou Web API, mas o conceito pode ser estendido para outros tipos de aplicação que estão além do mundo web.

HTTPS no Kestrel

Sempre quando pensamos em hospedar uma aplicação desenvolvida em ASP.NET logo nos vem a mente o IIS. Apesar de uma infinidade de recursos que ele possui, o novo ASP.NET traz nativamente uma forma de hospedar as aplicações em um processo fora do IIS (self-hosting), chamado de Kestrel, qual foi construído com a finalidade de ser multi-plataforma e vem ganhando bastante evidência com alguns testes que demonstram grandes resultados em termos de performance.

Aos poucos a Microsoft vem adicionado nele algumas funcionalidades para que ele incorpore tudo que é necessário para utilizarmos em ambientes de produção. A finalidade deste artigo é demonstrar como configurar a aplicação para suportar o uso do protocolo HTTPS e, consequentemente, proteger os recursos que serão expostos pela aplicação.

Como sabemos, independentemente da tecnologia ou servidor que utilizamos para as nossas aplicações, para o uso do HTTPS se faz necessário a compra de um certificado de alguma entidade publicamente conhecida, para que possamos instalar em nosso servidor e configurar a nossa aplicação para utiliza-lo para estabelecer um túnel seguro de comunicação entre o cliente e o servidor e vice-versa. Como o foco é demonstrar a configuração, vou utilizar um certificado de testes emitido por uma ferramenta, sem valor para uso em ambiente de produção.

O primeiro passa em nossa aplicação é indicar ao runtime que o hosting a ser utilizado é o Kestrel, e para isso, recorremos ao método UseKestrel. Em um dos seus overloads é possível acessar algumas configurações e entre elas a possibilidade de indicar que queremos que o HTTPS seja utilizado, que é expressado através do método de estensão UseHttps. Para fazer uso deste método é necessário a instalação de um pacote NUGET chamado Microsoft.AspNetCore.Server.Kestrel.Https.

Uma vez que o certificado está criado e armazenado em um arquivo PFX (que inclui a sua chave privada) e o pacote acima mencionado esteja instalado na aplicação, utilizamos o código abaixo para indicar o local onde o arquivo PFX referente ao certificado está localizado no disco; note também que o “12345” é a senha para que o ASP.NET possa acessar o certificado.

public class Program
{
    public static void Main(string[] args)
    {
        new WebHostBuilder()
            .UseKestrel(opts => opts.UseHttps(@”C:tempAppTesteMeuWebSite.pfx”, “12345”))
            .UseUrls(“http://localhost:5000/&#8221;, “https://localhost:5001/&#8221;)
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseStartup<Startup>()
            .Build()
            .Run();
    }
}

Ao rodar, a aplicação estará disponível para acesso tanto através do HTTP quanto HTTPS, porém em portas diferentes.

Transformação de Claims

Ao autenticar um usuário, nós podemos além de armazenar no token o seu nome, algumas outras propriedades que o descrevem, tais como: e-mail, papéis (roles), etc. Com isso, nós teremos diversas outras características dele além de apenas o nome e os papéis que ele possui dentro da aplicação.

Como sabemos, essas informações são expressadas através de claims. Ao autenticar, nós podemos criar uma coleção de claims contendo todas as informações sobre o respectivo usuário. Como as claims estão em todo lugar, o ASP.NET fornece um recurso específico que permite a transformação de claims, ou seja, além de utilizar informações que temos do lado do servidor para descrever o usuário, para complementar extraindo dados da requisição e incluir na coleção de claims.

Para customizar o tranformador, devemos implementar a interface IClaimsTransformer, e através do método TransformAsync podemos incrementar mais informações sobre o usuário e mais tarde utiliza-las para autorização de algum recurso específico. No exemplo abaixo, estamos extraindo a cultura (via header Accept-Language) da requisição e incluindo no ClaimsPrincipal gerado:

public class CultureToClaimTransformer : IClaimsTransformer
{
    public Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context)
    {
        var principal = context.Principal;

        if (principal.Identity.IsAuthenticated)
        {
            var culture = StringValues.Empty;

            if (context.Context.Request.Headers.TryGetValue(“Accept-Language”, out culture))
                ((ClaimsIdentity)principal.Identity).AddClaim(new Claim(“Culture”, culture.ToString()));
        }

        return Task.FromResult(principal);
    }
}

Só que a classe por si só não funciona. Precisamos incluir a mesma na execução, e para isso, recorremos ao método UseClaimsTransformation para indicar ao runtime do ASP.NET a classe que faz a transformação de claims. Depois do MVC devidamente configurado, estamos utilizando a autenticação baseada em cookies para o exemplo, indicamos a instância da classe CultureToClaimTransformer para a propriedade Transformer.

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication();
        services.AddMvc();
    }

    
public void Configure(IApplicationBuilder app)
    {
        app.UseCookieAuthentication(new CookieAuthenticationOptions()
        {
            LoginPath = “/Aplicacao/Login”,
            ReturnUrlParameter = “ReturnUrl”,
            AutomaticAuthenticate = true,
            AutomaticChallenge = true
        });

        app.UseClaimsTransformation(
new ClaimsTransformationOptions()
        {
            Transformer = new CultureToClaimTransformer()
        });

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: “default”,
                template: “{controller=Aplicacao}/{action=Index}/{id?}”);
        });
    }
}

Depois de toda a configuração realizada, nós vamos codificar o nosso controller. Ele possui apenas dois métodos: um que exibe informações e o outro que onde de fato realizamos o login. O método que exibe as informações (Index) está decorado com o atributo AuthorizeAttribute, que não permitirá usuários não autenticados acessá-lo. Já o segundo método serve para autenticar o usuário; em uma aplicação real, este método deve receber as informações postadas em um formulário para validar primeiramente se o usuário existe (em uma base de dados, por exemplo), e caso verdadeiro, aí sim devemos proceder com a autenticação.

public class
AplicacaoController : Controller
{
    [Authorize]
    public IActionResult Index()
    {
        return View();
    }

    
public IActionResult Login()
    {
        HttpContext.Authentication.SignInAsync(
            CookieAuthenticationDefaults.AuthenticationScheme,
            new ClaimsPrincipal(
                new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, “Israel Aece”) }, 
                CookieAuthenticationDefaults.AuthenticationScheme))).Wait();

        
return Redirect(Request.Query[“ReturnUrl”]);
    }
}

Por fim, ao rodar a aplicação e exibir a coleção de
claims do usuário logado, nós teremos duas: uma com o nome do usuário e a outra com a cultura que foi passada pelo navegador que o usuário está utilizando para acessar a aplicação:

Utilizando o HSTS

Quando falamos em proteger a comunicação entre a aplicação web e os clientes que a consomem, logo pensamos em HTTPS. Este protocolo tem a finalidade de criar um túnel entre as partes, garantindo com que as informações trafeguem entre os dois pontos mantendo a confidencialidade (ninguém além dos dois consegue visualizar) e a integridade (ninguém consegue alterar).

Quando habilitamos este protocolo na nossa aplicação e também no hosting que hospeda a mesma, não é 100% garantido que a aplicação será acessada através de HTTPS, colocando em risco a segurança, pois alguém no meio do caminho pode interceptar a requisição e explorar todo o seu conteúdo, e o pior, começar a direcionar o usuário para sites maliciosos.

Para um teste simples, considere o site do Banco Itaú. Muitas vezes as pessoas não se atentam em acessar o site digitando o https:// no navegador. Se monitorarmos a requisição para o endereço http://www.itau.com.br, nós veremos que o navegador recebe como resposta o código 301 (Moved Permanently), obrigando o navegador a encaminhar o cliente para o endereço seguro, que é o https://www.itau.com.br.

Ao receber este tipo de resposta e houver alguém interceptando a comunicação, ele pode substituir o endereço do redirecionamento e, consequentemente, nos mandar para um site malicioso, que simula a aparência do banco, e rouba todos os nossos dados. Para evitar isso, um novo recurso chamado HTTP Strict Transport Security (ou apenas HSTS) foi criado e permite à uma aplicação web indicar que ela somente pode ser acessada através do protocolo HTTPS, e em conjunto com o navegador, assegurarão que independentemente do que se tente fazer, a requisição sempre será feita (e as vezes até modificada para realizar) através do protocolo seguro. Para o servidor indicar ao navegador a obrigatoriedade da utilização do HTTPS, ele deve incluir no cabeçalho da resposta o item (chave) Strict-Transport-Security, indicando o tempo (em segundos) em que o cabeçalho será mantido do lado do cliente.

A especificação também contempla um parâmetro neste cabeçalho chamado includeSubDomains, que como o próprio nome indica, subdomínios deste site deverão ter a garantia de que também serão acessíveis somente através do HTTPS.

No ASP.NET 5, que é independente de hosting, podemos incluir este cabeçalho através de um middleware. É importante dizer que segundo a especificação, este cabeçalho só deve ser enviado ao cliente quando ele já estiver dentro de uma conexão protegida. E esta validação pode ser feita através da propriedade IsHttps que é exposta pelo objeto que representa a requisição.

public class HstsMiddleware
{
    private readonly RequestDelegate next;
    private readonly TimeSpan maxAge;

    public HstsMiddleware(RequestDelegate next, TimeSpan maxAge)
    {
        this.next = next;
        this.maxAge = maxAge;
    }

    public async Task Invoke(HttpContext context)
    {
        if (maxAge > TimeSpan.Zero && context.Request.IsHttps)
            context.Response.Headers.Append(
                “Strict-Transport-Security”, $”max-age={maxAge.TotalSeconds}”);

        await next(context);
    }
}

public static class HstsMiddlewareExtensions
{
    public static IApplicationBuilder UseHsts(this IApplicationBuilder builder, TimeSpan maxAge) => 
        builder.UseMiddleware<HstsMiddleware>(maxAge);
}

Alternativamente você também pode recorrer ao IIS para realizar a configuração do HSTS, e permitir com que os recursos que são disponibilizados por ele também sejam protegidos por HTTPS. A solução foi proposta pelo Doug Wilson e consiste em criar uma regra para sobrescrever o protocolo HTTP para HTTPS utilizando o arquivo de configuração da aplicação ou do servidor.

<?xml version=”1.0″ encoding=”UTF-8″?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name=”HTTP to HTTPS redirect” stopProcessing=”true”>
                    <match url=”(.*)” />
                    <conditions>
                        <add input=”{HTTPS}” pattern=”off” ignoreCase=”true” />
                    </conditions>
                    <action type=”Redirect” url=”https://{HTTP_HOST}/{R:1}”
                        redirectType=”Permanent” />
                </rule>
            </rules>
            <outboundRules>
                <rule name=”Add Strict-Transport-Security when HTTPS” enabled=”true”>
                    <match serverVariable=”RESPONSE_Strict_Transport_Security”
                        pattern=”.*” />
                    <conditions>
                        <add input=”{HTTPS}” pattern=”on” ignoreCase=”true” />
                    </conditions>
                    <action type=”Rewrite” value=”max-age=31536000″ />
                </rule>
            </outboundRules>
        </rewrite>
    </system.webServer>
</configuration>

Preload List

Apesar de garantir que todos os recursos da aplicação serão acessados através do HTTPS, o primeiro problema ainda persiste, ou seja, a requisição inicial para o site do banco pode continuar sendo feita através de HTTP, e a possibilidade de interceptação continua. Para evitar isso, o Google mantém um site (HSTS Preload List) em que podemos adicionar URLs que emitem este cabeçalho, e obviamente, possuem um certificado válido. Isso permitirá ao navegador antes de fazer a requisição para o site avaliar se ele está contido na lista, e se estiver, já transformará a primeira requisição em HTTPS.

Depois da primeira requisição feita, o navegador já é capaz de detectar a necessidade de acessar a aplicação através de HTTPS, e ao acessa-lo via HTTP, o próprio navegador faz um redirecionamento interno (307 – Internal Redirect) e já faz a requisição com HTTPS. É possível ver esse comportamento na imagem abaixo.