Ejercicio: uso de notificaciones con autorización basada en directivas
En esta unidad, se creará un nuevo usuario con privilegios administrativos. Se proporciona una demostración de la creación y el almacenamiento de notificaciones de usuario. También se define una directiva de autorización para determinar si un usuario autenticado tiene privilegios elevados en la interfaz de usuario.
Protección del catálogo de productos
La página catálogo de productos solo debe estar visible para los usuarios autenticados. Sin embargo, solo los administradores pueden editar, crear y eliminar productos.
En Pages/Products/Index.cshtml.cs, aplique los cambios siguientes:
Reemplace el comentario
// Add [Authorize] attributepor el atributo siguiente:[Authorize]El atributo anterior describe los requisitos de autenticación del usuario necesarios en la página. En este caso, no hay ningún requisito aparte de que el usuario se tenga que autenticar. Los usuarios anónimos no pueden ver la página y se redirigen a la página de inicio de sesión.
Quite la marca de comentario de la línea
//using Microsoft.AspNetCore.Authorization;en la parte superior del archivo.El cambio anterior resuelve el atributo
[Authorize]del paso anterior.Reemplace el comentario
// Add IsAdmin propertypor la propiedad siguiente:public bool IsAdmin => HttpContext.User.HasClaim("IsAdmin", bool.TrueString);El código anterior determina si el usuario autenticado tiene una notificación
IsAdmincon un valor deTrue. Se puede acceder al resultado de esta evaluación a través de una propiedad de solo lectura llamadaIsAdmin.En el método
OnDelete, reemplace el comentario// Add IsAdmin checkpor el código siguiente:if (!IsAdmin) { return Forbid(); }Cuando un empleado autenticado intenta eliminar un producto a través de la interfaz de usuario o mediante el envío manual de una solicitud HTTP DELETE a esta página, se devuelve un código de estado HTTP 403.
En Pages/Products/Index.cshtml, actualice los vínculos Editar, Eliminar y Agregar producto con el código resaltado:
Editar y eliminar vínculos:
<td> @if (Model.IsAdmin) { <a asp-page="Edit" asp-route-id="@product.Id">Edit</a> <span>|</span> <a href="#" onclick="deleteProduct('@product.Id', antiForgeryToken())">Delete</a> } </td>Vínculo Agregar producto:
@if (Model.IsAdmin) { <a asp-page="./Create">Add Product</a> }Los cambios anteriores hacen que los vínculos se representen únicamente cuando el usuario autenticado es un administrador.
Registro y aplicación de la directiva de autorización
Las páginas Crear producto y Editar producto deben ser accesibles solo para los administradores. Con el fin de encapsular los criterios de autorización para dichas páginas, se creará una directiva Admin.
En el método
ConfigureServicesde Startup.cs, realice los cambios siguientes:Reemplace el comentario
// Add call to AddAuthorizationpor el código siguiente:services.AddAuthorization(options => options.AddPolicy("Admin", policy => policy.RequireAuthenticatedUser() .RequireClaim("IsAdmin", bool.TrueString)));En el código anterior se define una directiva de autorización llamada
Admin. La directiva requiere que el usuario se autentique y tenga una notificaciónIsAdminestablecida enTrue.Incorpore el código resaltado siguiente:
services.AddAntiforgery(options => options.HeaderName = "X-CSRF-TOKEN"); services.AddRazorPages(options => options.Conventions.AuthorizePage("/Products/Edit", "Admin")); services.AddControllers();La llamada de método
AuthorizePageprotege la ruta de la página de Razor /Products/Edit aplicando la directivaAdmin. Una ventaja de este enfoque es que la página de Razor protegida no requiere ninguna modificación. En su lugar, el aspecto de la autorización se administra en Startup.cs. A los usuarios anónimos se les redirigirá a la página de inicio de sesión. A los usuarios autenticados que no cumplan los requisitos de la directiva se les aparece un mensaje de Acceso denegado.
En Pages/Products/Create.cshtml.cs, aplique los cambios siguientes:
Reemplace el comentario
// Add [Authorize(Policy = "Admin")] attributepor el atributo siguiente:[Authorize(Policy = "Admin")]El código anterior representa una alternativa a la llamada de método
AuthorizePageen Startup.cs. El atributo[Authorize]exige que se cumplan los requisitos de la directivaAdmin. A los usuarios anónimos se les redirigirá a la página de inicio de sesión. A los usuarios autenticados que no cumplan los requisitos de la directiva se les aparece un mensaje de Acceso denegado.Quite la marca de comentario de la línea
//using Microsoft.AspNetCore.Authorization;en la parte superior del archivo.El cambio anterior resuelve el atributo
[Authorize(Policy = "Admin")]del paso anterior.
Modificación de la página de registro
Modifique la página de registro para permitir que los administradores se registren mediante los pasos siguientes.
En Areas/Identity/Pages/Account/Register.cshtml.cs, realice los cambios siguientes:
Agregue la propiedad siguiente a la clase anidada
InputModel:public class InputModel { [DataType(DataType.Password)] [Display(Name = "Admin enrollment key")] public ulong? AdminEnrollmentKey { get; set; } [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 1)] [Display(Name = "First name")] public string FirstName { get; set; } [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 1)] [Display(Name = "Last name")] public string LastName { get; set; } [Required] [EmailAddress] [Display(Name = "Email")] public string Email { get; set; } [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "Password")] public string Password { get; set; } [DataType(DataType.Password)] [Display(Name = "Confirm password")] [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] public string ConfirmPassword { get; set; } }Aplique los cambios resaltados en el método
OnPostAsync:public async Task<IActionResult> OnPostAsync( [FromServices] AdminRegistrationTokenService tokenService, string returnUrl = null) { returnUrl = returnUrl ?? Url.Content("~/"); ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); if (ModelState.IsValid) { var user = new ContosoPetsUser { FirstName = Input.FirstName, LastName = Input.LastName, UserName = Input.Email, Email = Input.Email, }; var result = await _userManager.CreateAsync(user, Input.Password); if (result.Succeeded) { _logger.LogInformation("User created a new account with password."); await _userManager.AddClaimAsync(user, new Claim("IsAdmin", (Input.AdminEnrollmentKey == tokenService.CreationKey).ToString())); var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); var callbackUrl = Url.Page( "/Account/ConfirmEmail", pageHandler: null, values: new { area = "Identity", userId = user.Id, code = code }, protocol: Request.Scheme); await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>."); if (_userManager.Options.SignIn.RequireConfirmedAccount) { return RedirectToPage("RegisterConfirmation", new { email = Input.Email }); } else { await _signInManager.SignInAsync(user, isPersistent: false); return LocalRedirect(returnUrl); } } foreach (var error in result.Errors) { ModelState.AddModelError(string.Empty, error.Description); } } // If we got this far, something failed, redisplay form return Page(); }En el código anterior:
- El atributo
[FromServices]proporciona una instancia deAdminRegistrationTokenServicea partir del contenedor de IoC. - Se invoca el método
AddClaimAsyncde la claseUserManagerpara guardar una notificaciónIsAdminen la tablaAspNetUserClaims.
- El atributo
Agregue el código siguiente en la parte superior del archivo. Resuelve las referencias de clase
AdminRegistrationTokenServiceyClaimen el métodoOnPostAsync:using ContosoPets.Ui.Services; using System.Security.Claims;
En Areas/Identity/Pages/Account/Register.cshtml, agregue el marcado siguiente:
<div class="form-group"> <label asp-for="Input.ConfirmPassword"></label> <input asp-for="Input.ConfirmPassword" class="form-control" /> <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.AdminEnrollmentKey"></label> <input asp-for="Input.AdminEnrollmentKey" class="form-control" /> <span asp-validation-for="Input.AdminEnrollmentKey" class="text-danger"></span> </div> <button type="submit" class="btn btn-primary">Register</button>
Prueba de notificación de administrador
Ejecute el siguiente comando para compilar la aplicación:
dotnet build --no-restoreLa opción
--no-restorese incluye porque no se han agregado paquetes NuGet desde la última compilación. El proceso de compilación omite la restauración de paquetes NuGet y se realiza correctamente sin ninguna advertencia. Si se produce un error en la compilación, compruebe la salida con el fin de obtener información para solucionar problemas.Implemente la aplicación en Azure App Service ejecutando el comando siguiente:
az webapp upVaya a la aplicación e inicie sesión con un usuario existente, si aún no ha iniciado sesión. En el encabezado, seleccione Productos. Observe que al usuario no le aparecen vínculos para editar, eliminar o crear productos.
En la barra de direcciones del explorador, vaya directamente a la página Crear producto. Para obtener la dirección URL de esa página, ejecute el comando siguiente:
echo "$webAppUrl/Products/Create"Se prohíbe al usuario navegar a la página. Se muestra un mensaje de Acceso denegado. Del mismo modo, se prohibirá que el usuario navegue a una ruta como /Products/Edit/1.
Seleccione Cerrar sesión.
Obtenga un token de inscripción automática de administrador con el comando siguiente:
echo $(wget -q -O - $webAppUrl/admintoken)Advertencia
El mecanismo de inscripción automática de administrador se hace solo con fines ilustrativos. El punto de conexión /api/Admin que sirve para obtener un token debe protegerse antes de usarse en un entorno de producción.
En la aplicación web, registre un nuevo usuario. El token del paso anterior debe proporcionarse en el cuadro de texto Clave de inscripción de administrador.
Una vez que haya iniciado sesión con el nuevo usuario administrativo, haga clic en el vínculo Productos del encabezado.
El usuario administrativo puede ver, editar y crear productos.
Revisión de la tabla AspNetUserClaims
Ejecute el comando siguiente:
db -c 'SELECT u."Email", c."ClaimType", c."ClaimValue" FROM "AspNetUserClaims" AS c INNER JOIN "AspNetUsers" AS u ON c."UserId" = u."Id"'
Aparece una variación del resultado siguiente:
Email | ClaimType | ClaimValue
----------------------+-----------+------------
scott@contoso.com | IsAdmin | True
(1 row)
db -Q "SELECT u.Email, c.ClaimType, c.ClaimValue FROM dbo.AspNetUserClaims AS c INNER JOIN dbo.AspNetUsers AS u ON c.UserId = u.Id" -Y25 -y10
Aparece una variación del resultado siguiente:
Email ClaimType ClaimValue
------------------------- ---------- ----------
scott@contoso.com IsAdmin True
La notificación IsAdmin se almacena como un par clave-valor en la tabla AspNetUserClaims. El registro AspNetUserClaims está asociado con el registro de usuario en la tabla AspNetUsers.
¿Necesita ayuda? Consulte nuestra guía de solución de problemas o notifique un problema para enviar comentarios específicos.