ASP.NET Core ofrece un modelo de alojamiento simplificado, llamado API mínimas, que nos permite crear API ligeras con dependencias mínimas. Sin embargo, «mínima» no significa seguridad mínima. Las API mínimas también necesitan autenticación.
hemos explorado autenticación JWT en una publicación anterior aquí. En este artículo examinaremos cómo podemos crear un controlador de autenticación básico para API mínimas en ASP.NET Core. A continuación implementaremos un controlador de autenticación básico que identificará y autenticará al usuario. Debido a que validaremos la identidad del usuario utilizando credenciales almacenadas en una base de datos, haremos uso de Núcleo del marco de entidad.
Para utilizar los ejemplos de código proporcionados en este artículo, debe tener Visual Studio 2022 instalado en su sistema. Si aún no tienes una copia, puedes descargue Visual Studio 2022 aquí.
Cree un proyecto de API web ASP.NET Core en Visual Studio 2022
Para crear un proyecto de API web ASP.NET Core en Visual Studio 2022, siga los pasos que se describen a continuación.
- Inicie el IDE de Visual Studio 2022.
- Haga clic en «Crear nuevo proyecto».
- En la ventana «Crear nuevo proyecto», seleccione «ASP.NET Core Web API» de la lista de plantillas que se muestran.
- Haga clic en Siguiente.
- En la ventana «Configura tu nuevo proyecto», especifica el nombre y la ubicación del nuevo proyecto. Opcionalmente, marque la casilla de verificación «Colocar solución y proyecto en el mismo directorio», según sus preferencias.
- Haga clic en Siguiente.
- En la ventana «Información adicional» que se muestra a continuación, seleccione «.NET 8.0 (soporte a largo plazo)» como versión del marco y desmarque la casilla de verificación que dice «Usar controladores», ya que usaremos API mínimas en este proyecto.
- En otra parte de la ventana «Información adicional», deje el «Tipo de autenticación» configurado en «Ninguno» (el valor predeterminado) y asegúrese de que las casillas de verificación «Habilitar compatibilidad con Open API», «Configurar para HTTPS» y «Habilitar Docker» permanezcan sin marcar. . No utilizaremos ninguna de esas funciones aquí.
- Haga clic en Crear.
Usaremos este proyecto ASP.NET Core Web API para trabajar con los ejemplos de código que se proporcionan en las secciones siguientes.
Cree una API mínima en ASP.NET Core
Puede reemplazar el código generado con el siguiente fragmento de código para crear una API mínima básica.
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapGet("https://www.infoworld.com/", () => "Hello, World!"); app.Run();
Cuando ejecuta la aplicación, aparece el texto «¡Hola mundo!» se mostrará en su navegador web.
Habilite la autenticación en una API mínima
La autenticación es el proceso de determinar quién es el usuario y validar su identidad. (Una vez que el usuario está autenticado, podemos determinar los roles a los que el usuario debe tener acceso en la aplicación. Este proceso se conoce como autorización).
Puede habilitar la autenticación en una API mínima en ASP.NET Core utilizando el método AddAuthentication() como se muestra en el fragmento de código que se proporciona a continuación.
var builder = WebApplication.CreateBuilder(args); builder.Services.AddAuthentication(); var app = builder.Build(); app.MapGet("https://www.infoworld.com/", () => "Hello World!"); app.Run();
Instale el paquete EF Core NuGet
Usaremos las capacidades en memoria de Entity Framework Core para almacenar nuestras credenciales de usuario para la autenticación. Para agregar el paquete Microsoft.EntityFrameworkCore.InMemory a su proyecto, seleccione el proyecto en la ventana del Explorador de soluciones, luego haga clic derecho y seleccione «Administrar paquetes NuGet». En la ventana del Administrador de paquetes NuGet, busque el paquete Microsoft.EntityFrameworkCore.InMemory e instálelo.
Alternativamente, puede instalar el paquete a través de la consola del Administrador de paquetes NuGet ingresando el comando que se muestra a continuación.
PM> Install-Package Microsoft.EntityFrameworkCore.InMemory
Cree un nuevo DbContext en EF Core
DbContext es un componente integral de Entity Framework Core que representa una sesión de conexión con la base de datos. Cree una nueva clase denominada CustomDbContext extendiendo la clase DbContext de EF Core e ingrese el siguiente código allí.
public class CustomDbContext : DbContext { protected override void OnConfiguring (DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseInMemoryDatabase(databaseName: "IDGSampleDb"); } public DbSet<User> Users { get; set; } }
Crear una clase de usuario en ASP.NET Core
Cree una nueva clase llamada Usuario en un archivo llamado User.cs y escriba el siguiente código allí. Usaremos esta clase para almacenar nuestros usuarios y sus contraseñas para la autenticación.
public class User { public string Username { get; set; } public string Password { get; set; } }
Una instancia de nuestra clase Usuario aquí almacenará las credenciales de un usuario en la memoria. Normalmente, por supuesto, las credenciales de un usuario residirían permanentemente en la base de datos.
Cree una clase UserService para validar las credenciales de usuario
A continuación, creemos una clase llamada UserService que encapsula la lógica necesaria para validar las credenciales del usuario. El método Authenticate devuelve una instancia de la clase Usuario si las credenciales del usuario que se le pasan como parámetros son válidas.
El siguiente fragmento de código muestra la clase UserService.
public class UserService : IUserService { private readonly CustomDbContext _dbContext; public UserService(CustomDbContext customDbContext) { this._dbContext = customDbContext; } public async Task<User> Authenticate(string username, string password) { var user = await Task.Run(() => _dbContext.Users.SingleOrDefault (x => x.Username == username && x.Password == password)); return user; } }
La interfaz IUserService se proporciona a continuación para su referencia.
public interface IUserService { Task<User> Authenticate(string username, string password); }
Esquemas de autenticación y controladores de autenticación.
En ASP.NET Core, se utiliza un esquema de autenticación para especificar cómo se debe realizar la autenticación para una solicitud. Un esquema de autenticación comprende un conjunto con nombre de opciones y comportamientos que están encapsulados por un controlador de autenticación.
Un controlador de autenticación en ASP.NET Core es un tipo que implementa el comportamiento de un esquema de autenticación. Un controlador de autenticación amplía la interfaz IAuthenticationHandler o el tipo AuthenticationHandler
Cree un esquema de autenticación para una API mínima
Antes de crear un controlador de autenticación personalizado, primero debe crear un tipo de AuthenticationSchemeOptions personalizado como se muestra a continuación.
public class CustomAuthenticationSchemeOptions : AuthenticationSchemeOptions { public const string DefaultScheme = "BasicAuthentication"; public const string AuthorizationHeaderName = "Authorization"; }
Luego cree una nueva clase llamada Usuario en un archivo llamado User.cs e ingrese el siguiente código.
public class User { public string Username { get; set; } public string Password { get; set; } }
En el fragmento de código anterior, observe cómo hemos especificado el esquema de autenticación. Los esquemas de autenticación se utilizan para frustrar cualquier acceso no autorizado a información confidencial verificando la identidad de un usuario, dispositivo o entidad antes de otorgar acceso a un recurso. En este ejemplo, el esquema de autenticación predeterminado se ha especificado como BasicAuthentication.
En la autenticación básica, un cliente pasa credenciales en texto sin formato mientras realiza una solicitud HTTP a un servidor. El servidor devolverá un código de estado HTTP 401 no autorizado, que indica que la autenticación ha fallado, si la solicitud no es legítima. AuthorizationHeaderName indica el nombre del encabezado HTTP que se utilizará para transmitir las credenciales como parte de una solicitud HTTP.
Cree un controlador de autenticación para una API mínima
En ASP.NET Core, el método HandleAuthenticateAsync se usa en un controlador de autenticación para encapsular el código para autenticar una solicitud. Este método es parte de la clase AuthenticationHandler.
Debe implementar el método HandleAuthenticateAsync en su controlador de autenticación personalizado, incluido su código personalizado, para autenticar una solicitud. La siguiente lista de código muestra la implementación del método anulado HandleAuthenticateAsync.
protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { if (!Request.Headers.ContainsKey(CustomAuthenticationOptions.AuthorizationHeaderName)) { return AuthenticateResult.Fail("Unauthorized"); } var authenticationHeaderValue = Request.Headers[CustomAuthenticationOptions.AuthorizationHeaderName]; if (string.IsNullOrEmpty(authenticationHeaderValue)) { return AuthenticateResult.NoResult(); } User user; try { var authenticationHeader = AuthenticationHeaderValue.Parse(authenticationHeaderValue); var credentialBytes = Convert.FromBase64String(authenticationHeader.Parameter); var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2); var username = credentials[0]; var password = credentials[1]; user = new User() { Username = username, Password = password }; user = await _userService.Authenticate(username, password); if (user == null) return AuthenticateResult.Fail("Invalid Username or Password"); } catch { return AuthenticateResult.Fail("Invalid Authorization Header"); } var claims = new List<Claim>() { new Claim("Username", user.Username) }; var claimsIdentity = new ClaimsIdentity(claims, Scheme.Name); var claimsPrincipal = new ClaimsPrincipal(claimsIdentity); return AuthenticateResult.Success (new AuthenticationTicket(claimsPrincipal, this.Scheme.Name)); }
El método HandleAuthenticateAsync verifica si existe el encabezado de autorización. Si no existe, el controlador de autenticación devuelve una instancia de AuthenticateResult que indica un error. Si el encabezado de autorización está presente, el controlador recupera los datos presentes en el encabezado de autorización. Luego, estos datos se analizan para recuperar el nombre de usuario y la contraseña del usuario que se pasó en el encabezado de autorización de la solicitud HTTP.
Luego, las credenciales recuperadas se validan con la base de datos. Si las credenciales no son válidas, se devuelve una instancia de AuthorizationResult que indica el error. Si las credenciales son válidas, se crea una instancia de reclamos y luego se pasa un ticket de autorización. Luego, este ticket se devuelve utilizando una instancia de AuthenticateResult para permitir que los módulos restantes de la canalización se ejecuten como de costumbre.
Registre el controlador de autenticación en ASP.NET Core
Para registrar el controlador de autenticación personalizado con la canalización de procesamiento de solicitudes, debe incluir el siguiente fragmento de código en el archivo Program.cs.
builder.Services.AddAuthentication (CustomAuthenticationOptions.DefaultScheme) .AddScheme<CustomAuthenticationOptions, CustomAuthenticationHandler> (CustomAuthenticationOptions.DefaultScheme, options => { });
Por último, debe incluir el siguiente fragmento de código en el archivo Program.cs para aprovechar la autenticación y la autorización.
app.UseAuthentication(); app.UseAuthorization();
Ejemplo completo de controlador de autenticación en ASP.NET Core
El código fuente completo del controlador de autenticación personalizado se proporciona a continuación para su referencia.
public class CustomAuthenticationHandler : AuthenticationHandler<CustomAuthenticationOptions> { public CustomAuthenticationHandler (IOptionsMonitor<CustomAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { if (!Request.Headers.ContainsKey(CustomAuthenticationOptions.AuthorizationHeaderName)) { return AuthenticateResult.Fail("Unauthorized"); } var authenticationHeaderValue = Request.Headers[CustomAuthenticationOptions.AuthorizationHeaderName]; if (string.IsNullOrEmpty(authenticationHeaderValue)) { return AuthenticateResult.NoResult(); } User user = null; try { var authenticationHeader = AuthenticationHeaderValue.Parse(authenticationHeaderValue); var credentialBytes = Convert.FromBase64String(authenticationHeader.Parameter); var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2); var username = credentials[0]; var password = credentials[1]; user = new User() { Username = username, Password = password }; if (user == null) return AuthenticateResult.Fail("Invalid credentials"); } catch { return AuthenticateResult.Fail("Authorization Header is invalid"); } var claims = new List<Claim>() { new Claim("Username", user.Username) }; var claimsIdentity = new ClaimsIdentity(claims, Scheme.Name); var claimsPrincipal = new ClaimsPrincipal(claimsIdentity); return AuthenticateResult.Success (new AuthenticationTicket(claimsPrincipal, this.Scheme.Name)); } }
Cree un punto final HTTP para probar el controlador de autenticación
Considere el siguiente fragmento de código que muestra cómo puede crear un punto final HttpGet que requiera autorización. Este punto final se invocará solo si proporciona las credenciales correctas.
app.MapGet("/test", [Authorize] async ([FromBody] User user) => { var userName = user.Username; var password = user.Password; return Results.Ok(); });
Finalmente, ejecute tanto la aplicación como el herramienta cartero para invocar el punto final. La Figura 1 muestra cómo puede especificar el nombre de usuario y la contraseña para la solicitud en Postman.
Ahora puede invocar el punto final /test desde Postman. La Figura 2 muestra el punto final /test invocado desde Postman.
Si la autenticación es exitosa, el punto final API devolverá el código de estado HTTP 200 OK. Si la autenticación falla, el punto final API devolverá el código de estado HTTP 401 no autorizado.
Una implementación minimalista
Tenga en cuenta que nuestra implementación minimalista aquí no incluye ningún código para almacenar las credenciales del usuario en la base de datos. Debe escribir su propia implementación para aceptar las credenciales del usuario y luego almacenarlas en la base de datos subyacente. Además, en aras de la simplicidad, hemos utilizado aquí una base de datos en memoria. Por supuesto, debería utilizar un almacén persistente para las credenciales de usuario en una aplicación real.
Copyright © 2024 IDG Communications, Inc.