ASP .NET Core -  Implementando e consumindo JWT - II


Neste artigo veremos como implementar a autenticação usando o padrão aberto JSON Web Tokens em uma Web API ASP .NET Core e como consumir os serviços em uma aplicação Console.

Na primeira parte do artigo criamos nossa Web API e implementamos a autenticação via JSON Web Tokens gerando e validando o token de autenticação.

Neste artigo vamos mostrar como consumir a nossa API usando uma aplicação console C#.

'Bora' pra prática...

Recursos Usados:

Criando o projeto Console - Consumindo JWT

Abra o VS 2017 Community e clique em New Project;

Selecione Visual C# -> Windows Desktop e o template Console App(.NET Framework), informe o nome ConsomeApiUsers e clique em OK;

Dica:  Criar um projeto do tipo Console usando o .NET Core é mais performático e cria um projeto menor.

Incluindo as referências aos pacotes Nuget

Teremos que incluir dois pacotes Nuget em nosso projeto:

  1. Newtonsoft.Json - Para serializar/deserializar a resposta no formato JSON
  2. Microsoft.Extensions.Configuration.Json - Para obter uma instância de ConfigurationBuilder e assim acessar o arquivo de configuração appsettings.json que iremos usar no projeto

No menu Tools clique em Nuget Package Manager e a seguir em Manage Nuget Package for Solution e na guia Browse informe o nome de cada pacote e clique no botão Install.

Para consumir a nossa Web API criada no artigo anterior vamos precisar de algumas informações que iremos usar neste projeto para poder acessar a API.

Primeiro qual API vamos acessar ?

Vamos acessar a API ValuesController.

Qual a URI que temos que usar ?

Vamos usar a URI : https://localhost:44390/api/values

Para poder acessar esta API temos que obter o token de autenticação e para isso temos que fazer o login. As credenciais usadas para fazer o login são:

 "email" : "teste@yahoo.com",
 "password" :  "SenhaSecreta#2019"

Vamos então armazenar essas informações no nosso projeto Console criando um arquivo appsettings.json com o seguinte contéudo:

{
"API_Access": {
    "UrlBase": "https://localhost:44390/api/",
    "email": "teste@yahoo.com",
    "password": "SenhaSecreta#2019"
   } 
}

Neste arquivo definimos a chave API_Access, a partir da qual vamos acessasr as informações para url, email e password. Agora temos as informações que precisamos para acessar a API no arquivo appsettings.json.

Precisamos também uma forma de obter e armazenar o token que será gerado quando fizermos a autenticação via login.

Lembre que nossa API UsuariosController retorna os dados do token e da data de expiração da seguinte forma:

      return new UserToken()
      {
                Token = new JwtSecurityTokenHandler().WriteToken(token),
                Expiration = expiration
       };

Vamos então criar no projeto Console uma classe chamada Token que iremos usar para obter o token gerado.

Crie na raiz do projeto essa classe com o código a seguir:

  public class Token
    {
        public string AccessToken { get; set; }
        public string Expiration { get; set; }
    }

A parte da configuração esta pronta em nosso projeto console. Precisamos implementar :

Implementando a autenticação e a obtenção do token

No método Main() da classe Program vamos incluir o código abaixo:

      static void Main(string[] args)
        {
            var builder = new ConfigurationBuilder()
                  .SetBasePath(Directory.GetCurrentDirectory())
                  .AddJsonFile($"appsettings.json");

            var config = builder.Build();

            _urlBase = config.GetSection("API_Access:UrlBase").Value;

            var _uri = _urlBase + "usuarios/login";
            var _email = config.GetSection("API_Access:email").Value;
            var _password = config.GetSection("API_Access:password").Value;

            Console.WriteLine("Dados: uri - email e senha");
            Console.WriteLine($"{_uri} -  {_email} - {_password} \n");

            using (var client = new HttpClient())
            {

               //limpa o header

                client.DefaultRequestHeaders.Accept.Clear();

                //incluir o cabeçalho Accept que será envia na requisição             
                client.DefaultRequestHeaders.Accept.Add(
                    new MediaTypeWithQualityHeaderValue("application/json"));

                // Envio da requisição a fim de autenticar
                // e obter o token de acesso

                HttpResponseMessage respToken = client.PostAsync(_uri, new StringContent(
                        JsonConvert.SerializeObject(new
                        {
                            email = _email,
                            password = _password
                        }), Encoding.UTF8, "application/json")).Result;

                //obtem o token gerado
                string conteudo = respToken.Content.ReadAsStringAsync().Result;

                Console.WriteLine(conteudo + "\n");

                if (respToken.StatusCode == HttpStatusCode.OK)
                {
                    //deserializa o token e data de expiração para o objeto Token
                    Token token = JsonConvert.DeserializeObject<Token>(conteudo);

                        Console.WriteLine("A U T E N T I C A D O \n");

                        // Associar o token aos headers do objeto
                        // do tipo HttpClient

                        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken);

                        Console.WriteLine("Consultando a API - ValuesController");
                        Console.WriteLine("==========================================");

                        ConsultaValor(client, 2);
                }
            }
            Console.WriteLine("\nFinalizado!");
            Console.WriteLine("=============================== F I M  ========================");
            Console.ReadKey();
        }

Vamos entender o código:

A primeira parte do código a seguir define a leitura das informações obtidas no arquivo appsettings.json e sua exibição no console:

var builder = new ConfigurationBuilder()
                  .SetBasePath(Directory.GetCurrentDirectory())
                  .AddJsonFile($"appsettings.json");
            var config = builder.Build();
            _urlBase = config.GetSection("API_Access:UrlBase").Value;
            var _uri = _urlBase + "usuarios/login";
            var _email = config.GetSection("API_Access:email").Value;
            var _password = config.GetSection("API_Access:password").Value;
            Console.WriteLine("Dados: uri - email e senha");
            Console.WriteLine($"{_uri} -  {_email} - {_password} \n");

 

O código a seguir usa uma instância da classe HttpClient que fornece a classe base para enviar requisições HTTP e receber respostas HTTP a partir de uma fonte identificada por uma URI.

Assim ela inclui recursos para enviar requisições sobre HTTP bem como usar HttpRequestMessage e HttpResponseMessage para processar mensagens HTTP.

O código a seguir vai enviar uma requisição POST para a uri e usando as credenciais obtidas acima:

                //limpa o header
                client.DefaultRequestHeaders.Accept.Clear();

                //incluir o cabeçalho Accept que será envia na requisição             
                client.DefaultRequestHeaders.Accept.Add(
                    new MediaTypeWithQualityHeaderValue("application/json"));

                // Envio da requisição a fim de autenticar
                // e obter o token de acesso

                HttpResponseMessage respToken = client.PostAsync(_uri, new StringContent(
                        JsonConvert.SerializeObject(new
                        {
                            email = _email,
                            password = _password
                        }), Encoding.UTF8, "application/json")).Result;

                //obtem o token gerado
                string conteudo = respToken.Content.ReadAsStringAsync().Result;

                Console.WriteLine(conteudo + "\n");

O retorno será o token gerado que será exibido no console.

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
            services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

No código acima, incluimos também os provedores padrão de tokens para gerar os tokens.

Após obter o token verificamos se o código do status retornado é OK o que indica que a autenticação foi feita com sucesso.

Após isso associamos o token obtido no cabeçalho Authorization do Header da requisição e chamamos o método ConsultaValor(client,2) que vai consultar a API passando o parâmetro de valor igual a 2:

               if (respToken.StatusCode == HttpStatusCode.OK)
                {
                    //deserializa o token e data de expiração para o objeto Token
                    Token token = JsonConvert.DeserializeObject<Token>(conteudo);

                        Console.WriteLine("A U T E N T I C A D O \n");

                        // Associar o token aos headers do objeto
                        // do tipo HttpClient

                        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",
 token.AccessToken);

                        Console.WriteLine("Consultando a API - ValuesController");
                        Console.WriteLine("=============================================");

                        ConsultaValor(client, 2);
                }

O método ConsultaValor(client,2) vai consultar o método GET da API que espera um parâmetro inteiro. Abaixo temos o método que será consultado:

[Authorize]
// GET api/values/5
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
     return "abacaxi";
}

Implementando a consulta a API

Para implementar a consulta a API ValuesController precisamos saber a URI da consulta que será:

http://localhost/api/values/2

Vamos incluir na classe Program o código abaixo que vai realizar uma requisição GET, passando o token no header da requisição, para a uri definida e obter o valor retornado pela API:

        private static string _urlBase;

        private static void ConsultaValor(HttpClient client, int valor)
        {
            HttpResponseMessage response = client.GetAsync(_urlBase + "values/" + valor.ToString()).Result;

            Console.WriteLine("");
            Console.WriteLine($"{_urlBase} + 'values/' + {valor.ToString()}");
            Console.WriteLine("");

            if (response.StatusCode == HttpStatusCode.OK)
            {
                Console.WriteLine(response.Content.ReadAsStringAsync().Result);
            }
            else
            {
                Console.WriteLine("Token provavelmente expirado!");
            }

            Console.ReadKey();
        }

Executando o projeto iremos obter o seguinte resultado no console:

Estamos exibindo os dados da uri, email e senha bem como o token gerado e a uri usada na consulta e o resultado final obtido.

Pegue o projeto completo aqui: ConsomeApiUsers.zip

Bem-aventurados os limpos de coração, porque eles verão a Deus;
Bem-aventurados os pacificadores, porque eles serão chamados filhos de Deus;
Mateus 5:8,9

Referências:


José Carlos Macoratti