ASP.NET Core - Gerando um Json Web Tokens(JWT)
Neste artigo vamos recordar como implementar a autenticação JWT em aplicações ASP .NET Core. |
Vamos recordar como gerar o token JWT em uma projeto ASP .NET Core usando o VS Code.
Neste artigo vou usar uma abordagem mais simplificada e direta. Para saber mais sobre os tokens JWT consulte meus artigos:
Recursos usados:
A autenticação JWT
Para realizar a autenticação o usuário acessa um formulário de login e informa suas credenciais e envia envia os dados do usuário para um endpoint da API do servidor.
O servidor valida as credenciais do usuário, ele envia um token JWT codificado para o cliente. O token JWT é
basicamente um objeto JavaScript que pode conter alguns atributos do usuário
conectado. Ele pode conter o nome de usuário, email do usuário ou alguma outra informação útil
sobre o usuário.
No lado do cliente, armazenamos o token JWT no
armazenamento local do navegador(localStorage) para lembrar a sessão
de login do usuário.
Esse é o fluxo resumido da autenticação JWT que vamos implementar.
Vamos iniciar criando a aplicação WEB API usando a ASP .NET Core no ambiente do .NET 5.0 usando o NET CLI e o VS Code.
Criando a API
Para criar um novo projeto de Web API no ambiente .NET Core usando a NET CLI, precisamos:
Vamos abrir um terminal de comandos usando o PowerShell e criar um diretório labs, e entrando dentro desta pasta vamos emitir o comando : dotnet new webapi -n apiagenda
Este comando vai criar a pasta apiagenda e nesta pasta vai criar o projeto web api.
Temos assim o projeto web api - apiagenda - criado que vai exibir uma lista de agendamentos.
Podemos agora buildar o projeto e a seguir executar a api criada digitando os comandos:
Abrindo um navegador e digitando o comando: https://localhost:5001/swagger/index.html teremos o resultado a seguir:
Temos a nossa apiagenda exibindo um único endpoint usando a interface do Swagger que foi incluída por padrão no template padrão que usamos para criar o projeto. Esse endpoint usa o controlador padrão criado que podemos até remover do projeto pois não vamos usá-lo.
Vamos criar um novo controlador para exibir uma lista de agendamentos.
Vamos abrir o projeto no VS Code digitando o comando : code .
Abaixo vemos o projeto aberto no VS Code:
Agora crie uma pasta Models no projeto e nesta pasta crie a classe Agenda:
public class Agenda { public int Id { get; set; } public string Evento { get; set; } public DateTime Data { get; set; } } |
A seguir crie um novo controlador na pasta Controllers chamado AgendaController com o seguinte código :
using System.Collections.Generic; using apiagenda.Models; using Microsoft.AspNetCore.Mvc; namespace apiagenda.Controllers |
Executando novamente o projeto e acessando o endpoint api/agenda temos o resultado:
Assim vemos que o ao acessar o endpoint api/agenda podemos obter a lista de agendamentos.
Vamos então iniciar a configuração para implementar a autenticação JWT.
Configurando a autenticação JWT
Para configurar a autenticação JWT no .NET Core, precisamos modificar o código do método ConfigureServices da classe Startup do projeto. Esta classe realiza a inicialização da aplicação e assim vamos habilitar a autenticação JWT.
Para isso temos que incluir no projeto o pacote: Microsoft.AspNetCore.Authentication.JwtBearer que contém recursos que habilitam o suporte para a autenticação baseada no JWT.
Abra um terminal de comandos, posicione-se na pasta do projeto e digite:
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
Após isso podemos modificar o método ConfigureServices incluindo o código em destaque para implementar a autenticação JWT:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(opt => {
opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
});
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "apiagenda", Version = "v1" });
});
}
|
Neste código :
Invocamos o middleware AddAuthentication() e especificamos o esquema do portador JWT para ser o esquema de autenticação. Também especificamos várias opções para o esquema do portador(bearer) JWT.
Se você observar o objeto TokenValidationParameters, verá que ele indica se o emissor, a audiência, a vida útil e a chave de assinatura devem ser validados ou não. No exemplo usamos definindo como true.
Além disso, também especificamos um emissor válido, um público-alvo válido e uma chave de assinatura válida. Esses valores serão recuperados a partir do arquivo de configuração appsettings.json que vamos definir a seguir.
Abra o arquivo appsettings.json e inclua uma seção chamada Jwt (o nome pode ser qualquer um) no arquivo conforme mostrado a seguir:
{
"Jwt": {
"Key": "aqui voce usa uma chaveSecreta para ser usada para assinar o token",
"Issuer": "AlgumIssuer",
"Audience": "AlgumaAudience"
},
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}
|
-
Key é uma string secreta que é usada para
assinatura do token;
- Issuer - Indica a parte que esta emitindo o JWT;
- Audience - Indica os destinatários do JWT;
Agora temos que habilitar o middleware de autenticação incluindo o código abaixo em azul no método Configure da mesma classe:
public void Configure(IApplicationBuilder
app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "apiagenda v1")); } app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
|
No código chamamos UseAuthentication() para conectar o middleware de autenticação ao pipeline HTTP.
Definindo a segurança dos endpoints da API
Vamos incluir o atributo [Authorize] no método Action GetAgendamentos da API de forma a restringir o acesso a somente usuários autenticados. Dessa forma somente os usuários que fizerem o Login válido poderão obter uma lista de agendamentos.
using System.Collections.Generic;
using apiagenda.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace apiagenda.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class AgendaController : ControllerBase
{
[HttpGet,Authorize]
public IActionResult GetAgendamentos()
{
List<Agenda> agenda = new List<Agenda>();
agenda.Add(new Agenda()
{
Id = 1,
Evento = "Curso DDD na prática",
Data = System.DateTime.Now
});
agenda.Add(new Agenda()
{
Id = 1,
Evento = "Evento - Design Patterns",
Data = System.DateTime.Now.AddDays(15)
});
agenda.Add(new Agenda()
{
Id = 1,
Evento = "Palestra - Os recursos do .NET 6.0",
Data = System.DateTime.Now.AddDays(30)
});
return new ObjectResult(agenda);
}
}
}
|
Agora ao tentar acessar o endpoint api/agenda iremos obter o erro : 401 Unauthorized
Precisamos então implementar o login para que o usuário possa se autenticar e acessar o endpoint.
Implementando o login
Para autenticar
usuários anônimos, temos que fornecer um endpoint de Login em nossa API
para que os usuários possam fazer login e acessar recursos protegidos. Um
usuário fornecerá um nome de usuário, uma senha e, se as credenciais forem
válidas, emitiremos um token JWT para o cliente solicitante.
Para isso vamos adicionar uma classe LoginViewModel
para manter as credenciais do usuário no servidor. A classe
LoginViewModel é uma classe simples que contém duas
propriedades: UserName e Password. Vamos criar essa
classe na pasta Models do projeto:
public class LoginViewModel
{
public string UserName { get; set; }
public string Password { get; set; }
}
|
A seguir vamos criar o controlador AuthenticationController na pasta Controllers onde vamos validar as credenciais do usuário. Se as credenciais forem válidas, emitiremos um token JWT. Para esta demonstração, vamos codificar o nome de usuário como 'macoratti' e a senha como 'Numsey#2021' para implementar um usuário fake e assim simplificar o exemplo. (Aqui poderíamos implementar o Identity e armazenar as credenciais do usuário em um banco de dados.)
Depois de validar as credenciais do usuário, vamos gerar um JWT com uma chave secreta. O JWT usa a chave secreta para gerar a assinatura. E essa chave secreta esta definida no arquivo appsettings.json.
Implementando o controlador AuthenticationController:
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using apiagenda.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
namespace apiagenda.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class AuthenticationController : ControllerBase
{
private IConfiguration _config;
public AuthenticationController(IConfiguration Configuration)
{
_config = Configuration;
}
[HttpPost, Route("login")]
public IActionResult Login([FromBody]LoginViewModel user)
{
if (user == null)
{
return BadRequest("Request do cliente inválido");
}
if (user.UserName == "macoratti" && user.Password == "Numsey#2021")
{
var _secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var _issuer = _config["Jwt:Issuer"];
var _audience = _config["Jwt:Audience"];
var signinCredentials = new SigningCredentials(_secretKey, SecurityAlgorithms.HmacSha256);
var tokeOptions = new JwtSecurityToken(
issuer : _issuer,
audience: _audience,
claims: new List<Claim>(),
expires: DateTime.Now.AddMinutes(2),
signingCredentials: signinCredentials);
var tokenString = new JwtSecurityTokenHandler().WriteToken(tokeOptions);
return Ok(new { Token = tokenString });
}
else
{
return Unauthorized();
}
}
}
}
|
Entendendo o
código :
Criamos um método Action POST usando o verbo HttPost
que vai enviar as credenciais para validação para o servidor.
No
método de
Login, estamos criando um
SymmetricSecretKey com o valor da chave secreta _secretKey
definida no arquivo appsettings.json. Em seguida,
estamos criando o objeto SigningCredentials e, como
argumentos, fornecemos uma chave secreta e o nome do algoritmo que vamos
usar para encriptar o token.
As duas primeiras etapas são as etapas padrão com as quais você não precisa se
preocupar. A terceira etapa é aquela em que estamos interessados. Na terceira
etapa, estamos criando o objeto JwtSecurityToken
com alguns parâmetros importantes:
A seguir criamos uma representação string do token JWT chamando o método WriteToken em JwtSecurityTokenHandler. Por fim, estamos retornando o JWT em uma response. Como resposta, criamos um objeto anônimo que contém apenas a propriedade Token.
Agora é só alegria, vamos testar...
Executando o projeto no terminal do VS Code.
A seguir informando o userName - macoratti e password - Numsey#2021 e clicando em Execute teremos o seguinte resultado:
Vemos o token gerado, e, fazendo o download temos o token exibido a seguir:
{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjM1NDA3MTIsImlzcyI6Imh0dHBzOi8vb G9jYWxob3N0OjUwMDEiLCJhdWQiOiJodHRwczovL2xvY2FsaG9zdDo1MDAxIn0.F7WWmzYu8-GU-216 yCS6RFUHW4Fic-rN3HxlnAZeL0U" } |
Assim neste artigo vimos como gerar um token JWT de forma simples em uma aplicação ASP .NET Core.
Pegue o projeto implementado aqui: apiagenda1.zip (sem as referências)
"Portanto,
lembrai-vos de que vós noutro tempo éreis gentios na carne, e chamados
incircuncisão pelos que na carne se chamam circuncisão feita pela mão dos
homens;
Que naquele tempo estáveis sem Cristo, separados da comunidade de Israel, e
estranhos às alianças da promessa, não tendo esperança, e sem Deus no mundo."
Efésios 2:11,12
Referências:
ASP .NET Core 2 - MiniCurso Básico
ASP .NET Core - Macoratti
Conceitos - .NET Framework versus .NET Core
ASP .NET Core - Conceitos Básicos
ASP .NET Core MVC - CRUD básico com ADO .NET
ASP .NET Core - Implementando a segurança com .
ASP .NET Core - Apresentando Razor Pages
Minicurso ASP .NET Core 2.0 - Autenticação com JWT - I
Minicurso ASP .NET Core 2.0 - Autenticação com JWT - II