asp.net - tutorial - web forms c#
¿Cómo puedo obtener un rol de usuario dentro de un método WebAPI sin una búsqueda en la tabla AspNetUserRoles? (3)
Tengo un procedimiento almacenado que actualiza el estado. Dependiendo de la función del usuario, el procedimiento almacenado tiene un código que puede o no permitir el cambio de estado. Por esta razón, necesito pasar un nombre de rol a un procedimiento almacenado. El nombre de mi rol se almacena en el cliente en mi código javascript pero, por supuesto, necesito una segunda verificación en el servidor. Cada usuario tiene solo uno de los tres roles y al solicitar una actualización de estado, puedo llamar a uno de los tres métodos dependiendo del rol que tenga el cliente. Esto es lo que he intentado.
Estoy utilizando WebApi con autenticación basada en token de portador y ASP.NET Identity 2.1 y la aplicación siempre se ejecuta en un navegador. Mis usuarios han sido configurados con los roles apropiados.
Coloco un código para obtener el ID de usuario y luego voy a la tabla AspNetUserRoles para obtener el rol al inicio de un método. Sin embargo, noté que esto toma alrededor de 500 milisegundos para ejecutarse. Como alternativa estoy considerando lo siguiente:
[HttpPut]
[Authorize(Roles = "Admin")]
[Route("AdminUpdateStatus/{userTestId:int}/{userTestStatusId:int}")]
public async Task<IHttpActionResult> AdminUpdateStatus(int userTestId, int userTestStatusId)
{
return await UpdateStatusMethod(userTestId, userTestStatusId, "Admin");
}
[HttpPut]
[Authorize(Roles = "Student")]
[Route("StudentUpdateStatus/{userTestId:int}/{userTestStatusId:int}")]
public async Task<IHttpActionResult> StudentUpdateStatus(int userTestId, int userTestStatusId)
{
return await UpdateStatusMethod(userTestId, userTestStatusId, "Student");
}
[HttpPut]
[Authorize(Roles = "Teacher")]
[Route("TeacherUpdateStatus/{userTestId:int}/{userTestStatusId:int}")]
public async Task<IHttpActionResult> TeacherUpdateStatus(int userTestId, int userTestStatusId)
{
return await UpdateStatusMethod(userTestId, userTestStatusId, "Teacher");
}
private async Task<IHttpActionResult> UpdateStatusMethod(int userTestId, int userTestStatusId, string roleName)
{
// Call the stored procedure here and pass in the roleName
}
¿Es esta una forma eficiente de hacer esto o hay quizás otra forma más limpia? Lo que no tengo claro es si el extremo frontal o posterior almacena en caché la función de los usuarios. Supongo que esto se hace o hay algún ajuste que permitirá que esto se haga.
Tenga en cuenta que estoy utilizando notificaciones para enviar la información de la función a mi cliente aquí:
public static AuthenticationProperties CreateProperties(
string userName,
ClaimsIdentity oAuthIdentity,
string firstName,
string lastName,
int organization)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "userName", userName},
{ "firstName", firstName},
{ "lastName", lastName},
{ "organization", organization.ToString()},
{ "roles",string.Join(":",oAuthIdentity.Claims.Where(c=> c.Type == ClaimTypes.Role).Select(c => c.Value).ToArray())}
};
return new AuthenticationProperties(data);
}
Sin embargo, mi pregunta aquí se relaciona con el servidor y cómo puedo verificar mi método si un usuario está en un determinado rol sin ir a la base de datos. Tal vez haya una manera de hacer esto de manera segura con reclamos, pero no sé cómo hacerlo.
Cualquier ayuda y consejo sería muy apreciado.
Como dijo, está utilizando tokens de portador para proteger sus puntos finales. Creo que hay pocos malentendidos con lo que contienen esas fichas mágicas de portador en su interior. Bueno, esos tokens contienen todos los roles para el usuario para el que emitió el token, además, si está utilizando el DPAPI de protección de datos predeterminado en la API web (tokens JWT), esos tokens están firmados y encriptados para que nadie pueda manipular los datos. dentro del token, a menos que tenga la clave mashine para el servidor web, este token no debe preocuparse por la protección de datos.
Mi recomendación es leer los roles / reclamos para el usuario de la base de datos, no hay necesidad de estas soluciones y trucos que intenta hacer, todo lo que necesita hacer es establecer los reclamos para los usuarios cuando inician sesión en el método GrantResourceOwnerCredentials
Puede configurarlo de esta manera haciendo que el usuario lea las funciones de DB y las establezca como tipo de "Función"
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
identity.AddClaim(new Claim(ClaimTypes.Role, "Supervisor"));
Recuerde que esto solo sucede una vez que el usuario inicia sesión, luego recibirá un token firmado y ecrypted del portador que contiene todas las reclamaciones de este usuario, sin necesidad de acceso a DB para verificarlo.
O si desea crear la identidad de la base de datos, puede utilizar lo siguiente:
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager, string authenticationType)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
// Add custom user claims here
return userIdentity;
}
Luego, en GrantResourceOwnerCredentials
haga lo siguiente:
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, OAuthDefaults.AuthenticationType);
Ahora, una vez que envíe el token de portador a un punto final protegido con Authorize
atributo de Authorize
como [Authorize(Roles = "Teacher")]
puedo asegurarle que su código no irá a la base de datos para hacer una consulta, abrir el generador de perfiles de SQL y verificarlo leerá los reclamos del token cifrado junto con el reclamo de roles y verificará si este usuario pertenece al rol de profesor y permitirá o denegará la solicitud.
He publicado una serie detallada de 5 publicaciones sobre la autenticación basada en token junto con el servidor de autorización y tokens JWT . Le recomiendo que lea esas publicaciones para comprender mejor las fichas de portador.
En lugar de tener 3 métodos separados, simplemente puede verificar el User.IsInRole("RoleName")
disponible en su clase de controlador. Está utilizando la misma lógica que está en el atributo [Authorise]
. p.ej
public class DataApiController : ApiController
{
[HttpPut]
[Route("UpdateStatus/{userTestId:int}/{userTestStatusId:int}")]
public async Task<IHttpActionResult> UpdateStatus(int userTestId, int userTestStatusId)
{
if(User.IsInRole("Admin"))
{
//Update method etc....
}
//else if(....) else etc
{
}
Los reclamos de rol de forma predeterminada se almacenan en la cookie de inicio de sesión del usuario, por lo que [Autorizar (Roles = "Profesor")] debería ser rápido. El único acceso a la base de datos será cuando inicie sesión por primera vez (o cuando se actualice la cookie de inicio de sesión. Las comprobaciones de autorización se realizan contra IClaimsPrincipal creado a partir de la cookie de inicio de sesión.
Es posible que también deba actualizar algún otro código para que esto funcione, consulte esta pregunta que es similar a lo que está haciendo: Autorizar con roles