Implementación de Autenticación JWT en ASP.NET Core 3.1 WebAPI

1: Importar el paquete NuGet Microsoft.AspNetCore.Authentication.JwtBearer

2: Configuración de parámetros JWT

3: Configuración en Startup

 1 public void ConfigureServices(IServiceCollection services){
 2 #region Configuración de Autenticación JWT
 3 services
 4 .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
 5 .AddJwtBearer(options => {
 6 var configData = AppConfigurationHelper.LoadConfiguration();
 7 options.TokenValidationParameters = new TokenValidationParameters
 8 {
 9 ValidIssuer = configData.Issuer,
10 ValidAudience = config.Audience,
11 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config.SecretKey)),
12 ClockSkew = TimeSpan.Zero
13 };
14 });
15 #endregion
16 }
17 
18 // Asegurarse de que está antes de AddMvc()
19 services.AddMvc();
20 
21 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
22 {
23 app.UseAuthentication();
24 app.UseAuthorization();
25 }

Ver código4: Utilizar atributos en Controladores y Acciones

Se puede utilizar el atributo [Authorize] individualmente en cada Action. Para omitir la autenticación, utiliza el atributo [AllowAnonymous]. Ambos atributos se encuentran en el siguiente namespace: using Microsoft.AspNetCore.Authorization;

5: Implementación del inicio de sesión y generación de tokens

Después de un inicio de sesión exitoso, el backend debe devolver el token generado. Puedes probarlo con Postman o en el sitio web oficial JWT.io.

6: Anviar solicitudes al backend con el token

Ejemplo de solicitud GET: ://localhost:5000/usuario/login Cabecera (Header) Authorization Bearer qweTdfdsfsJhdsfd0.fdsfdsgfdsewDDQDD.fdsfdsg***

7: Código de ejemplo para acciones

 1 [HttpPost, Route("Login")]
 2         public ApiResponse Login(usuario usuario)
 3         {
 4             ApiResponse respuesta = new ApiResponse();
 5             try
 6             {
 7                 string tokenString = AutenticacionJwtHelper.GenerarToken(usuario);
 8                 respuesta.data = tokenString;
 9                 respuesta.codigo = EstadoExito.Ok;
10                 respuesta.mensaje = "Token generado exitosamente!";
11             }
12             catch (Exception ex)
13             {
14                 respuesta.mensaje = "Error en la autenticación: " + ex.Message;
15             }
16             return respuesta;
17         }
18 
19 
20         [HttpPost, Route("verificarAutenticacion")]
21         [Authorize]
22         [AllowAnonymous] // Para omitir autenticación cuando sea necesario
23         public ApiResponse verificarAutenticacion(string tokenAcceso)
24         {
25             ApiResponse respuesta = new ApiResponse();
26             try
27             {
28                 var datosToken = AutenticacionJwtHelper.DecodificarToken(tokenAcceso);
29                 respuesta.data = datosToken;
30                 respuesta.codigo = EstadoExito.Ok;
31                 respuesta.mensaje = "Verificación exitosa!";
32             }
33             catch (Exception ex)
34             {
35                 respuesta.mensaje = "Error en la verificación: " + ex.Message;
36             }
37             return respuesta;
38         }

Ver código8: Implementación completa del helper JWT

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Threading.Tasks;
  5 namespace ProyectoWeb.Utilidades
  6 {
  7     using Microsoft.AspNetCore.Http;
  8     using Microsoft.IdentityModel.Tokens;
  9     using System.IdentityModel.Tokens.Jwt;
 10     using System.Security.Claims;
 11     using System.Text;
 12     using ProyectoWeb.Modelos;
 13 
 14     /// <summary>
 15     /// Helper para la generación y validación de JWT tokens
 16     /// </summary>
 17     public class AutenticacionJwtHelper
 18     {
 19         /// <summary>
 20         /// Genera un token JWT
 21         /// </summary>
 22         /// <param name="usuario">Datos del usuario</param>
 23         /// <returns>Token JWT como string</returns>
 24         public static string GenerarToken(usuario usuario)
 25         {
 26             var config = ConfiguracionApp.ObtenerConfiguracion();
 27             string emisor = config.Emisor;
 28             string audiencia = config.Audiencia;
 29             string claveSecreta = config.ClaveSecreta;
 30 
 31             var credencialesFirma = new SigningCredentials(
 32                 new SymmetricSecurityKey(Encoding.ASCII.GetBytes(claveSecreta)), 
 33                 SecurityAlgorithms.HmacSha256
 34             );
 35 
 36             var claims = new Claim[] {
 37                 new Claim(JwtRegisteredClaimNames.Sid, usuario.IdUsuario),
 38                 new Claim(JwtRegisteredClaimNames.Iss, emisor),
 39                 new Claim(JwtRegisteredClaimNames.Sub, usuario.Nombre),
 40                 new Claim("IdentificadorUnico", Guid.NewGuid().ToString("D")),
 41                 new Claim("IdRol", usuario.IdRol.ToString()),
 42                 new Claim("Edad", usuario.Edad.ToString()),
 43                 new Claim("FechaNacimiento", usuario.FechaNacimiento.ToString())
 44             };
 45 
 46             var tokenSeguridad = new JwtSecurityToken(
 47                 emisor: emisor,
 48                 audiencia: audiencia,
 49                 credencialesFirma: credencialesFirma,
 50                 expires: DateTime.Now.AddMinutes(2),
 51                 claims: claims
 52             );
 53 
 54             return new JwtSecurityTokenHandler().WriteToken(tokenSeguridad);
 55         }
 56 
 57         /// <summary>
 58         /// Obtiene el token del encabezado de autorización
 59         /// </summary>
 60         /// <param name="context">Contexto HTTP</param>
 61         /// <returns>Token como string</returns>
 62         public static string ObtenerToken(HttpContext context)
 63         {
 64             return context != null ? context.Request.Headers["Authorization"].ToString() : "";
 65         }
 66 
 67         /// <summary>
 68         /// Decodifica y valida un token JWT
 69         /// </summary>
 70         /// <param name="tokenAcceso">Token a decodificar</param>
 71         /// <returns>Información del token</returns>
 72         public static InformacionToken DecodificarToken(string tokenAcceso)
 73         {
 74             try
 75             {
 76                 if (tokenAcceso.Contains("Bearer"))
 77                 {
 78                     tokenAcceso = tokenAcceso.Replace("Bearer ", "");
 79                 }
 80                 
 81                 var manejadorToken = new JwtSecurityToken(tokenAcceso);
 82                 InformacionToken infoToken = new InformacionToken
 83                 {
 84                     IdUsuario = manejadorToken.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Sid).Value,
 85                     Nombre = manejadorToken.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Sub).Value,
 86                     Edad = manejadorToken.Claims.FirstOrDefault(c => c.Type == "Edad").Value,
 87                     FechaNacimiento = manejadorToken.Claims.FirstOrDefault(c => c.Type == "FechaNacimiento").Value,
 88                     IdRol = manejadorToken.Claims.FirstOrDefault(c => c.Type == "IdRol").Value,
 89                 };
 90                 return infoToken;
 91             }
 92             catch (Exception ex)
 93             {
 94                 throw new Exception("Error al decodificar el token: " + ex.Message);
 95             }
 96         }
 97     }
 98     
 99     public class InformacionToken
100     {
101         public string IdUsuario { get; set; }
102         public string Nombre { get; set; }
103         public string Edad { get; set; }
104         public string FechaNacimiento { get; set; }
105         public string IdRol { get; set; }
106     }
107 }

Ver código9: Entidad del modelo

 1 using System;
 2 using System.Collections.Generic;
 3 using System.ComponentModel.DataAnnotations;
 4 
 5 namespace ProyectoWeb.Modelos
 6 {
 7     [Serializable]
 8     public class usuario
 9     {
10         [Required(ErrorMessage = "El nombre es obligatorio")]
11         [StringLength(maximumLength: 50, ErrorMessage = "El nombre no puede superar 50 caracteres")]
12         [MinLength(2, ErrorMessage = "El nombre debe tener al menos 2 caracteres")]
13         public string Nombre { get; set; }
14 
15         [Range(1, 150, ErrorMessage = "La edad debe estar entre 1 y 150")]
16         public int Edad { get; set; }
17         
18         [DataType(DataType.Date, ErrorMessage = "La fecha debe tener formato válido (ej: 1990-01-01)")]
19         public DateTime FechaNacimiento { get; set; }
20 
21         [Required(ErrorMessage = "La contraseña es obligatoria")]
22         [StringLength(maximumLength: 20, MinimumLength = 6, ErrorMessage = "La contraseña debe tener entre 6 y 20 caracteres")]
23         public string Contraseña { get; set; }
24         
25         public int IdRol { get; set; }
26         public string IdUsuario { get; set; }
27     }
28 }

Ver código10: Contenido de configuración:

11: Efectos de la prueba

Nota final: Por seguridad y optimización de recursos, generalmente solo incluimos identificadores de datos no sensibles en el token

Etiquetas: ASP.NET-Core JWT WebApi autenticación seguridad

Publicado el 6-27 18:48