c# - route - tag helpers asp net core
Cómo implementar el control de acceso basado en permisos con Asp.Net Core (3)
Para obtener una solución que no requiera que agregue una política para cada permiso, vea mi answer para otra pregunta.
Le permite decorar sus Controladores y Acciones con cualquier atributo personalizado que desee, y acceder a ellos en su AuthorizationHandler.
Estoy tratando de implementar el control de acceso basado en permisos con aspnet core. Para gestionar dinámicamente los roles y permisos de usuario (create_product, delete_product, etc.), se almacenan en la base de datos. El modelo de datos es como http://i.stack.imgur.com/CHMPE.png
Antes de aspnet core (en MVC 5) estaba usando
AuthorizeAttribute
personalizado como a continuación para manejar el problema:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
private readonly string _permissionName { get; set; }
[Inject]
public IAccessControlService _accessControlService { get; set; }
public CustomAuthorizeAttribute(string permissionName = "")
{
_permissionName = permissionName;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
var user = _accessControlService.GetUser();
if (PermissionName != "" && !user.HasPermission(_permissionName))
{
// set error result
filterContext.HttpContext.Response.StatusCode = 403;
return;
}
filterContext.HttpContext.Items["CUSTOM_USER"] = user;
}
}
Luego lo estaba usando en el método de acción como a continuación:
[HttpGet]
[CustomAuthorize(PermissionEnum.PERSON_LIST)]
public ActionResult Index(PersonListQuery query){ }
Además, estaba usando HttpContext.Items ["CUSTOM_USER"] en las vistas para mostrar u ocultar la parte html:
@if (CurrentUser.HasPermission("<Permission Name>"))
{
}
Cuando decidí cambiar el núcleo de aspnet, todo mi plan falló.
Porque no había un método virtual de
OnAuthorization
en
AuthorizeAttribute
.
Intenté algunas formas de resolver el problema.
Esos están abajo:
-
Uso de una nueva autorización basada en políticas (creo que no es adecuada para mi escenario)
-
Uso de
AuthorizeAttribute
yAuthorizationFilter
(leí esta publicación https://stackoverflow.com/a/35863514/5426333 pero no pude cambiarlo correctamente) -
Uso de middleware personalizado (¿cómo obtener
AuthorizeAttribute
de la acción actual?) -
Uso de ActionFilter (¿es correcto por motivos de seguridad?)
No pude decidir cuál es la mejor opción para mi escenario y cómo implementarlo.
Primera pregunta : ¿La implementación de MVC5 es una mala práctica?
Segunda pregunta : ¿Tiene alguna sugerencia para implementar aspnet core?
Según los comentarios, aquí hay un ejemplo sobre cómo usar la autorización basada en políticas:
public class PermissionRequirement : IAuthorizationRequirement
{
public PermissionRequirement(PermissionEnum permission)
{
Permission = permission;
}
public PermissionEnum Permission { get; }
}
public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
{
private readonly IUserPermissionsRepository permissionRepository;
public PermissionHandler(IUserPermissionsRepository permissionRepository)
{
if(permissionRepository == null)
throw new ArgumentNullException(nameof(permissionRepository));
this.permissionRepository = permissionRepository;
}
protected override void Handle(AuthorizationContext context, PermissionRequirement requirement)
{
if(context.User == null)
{
// no user authorizedd. Alternatively call context.Fail() to ensure a failure
// as another handler for this requirement may succeed
return null;
}
bool hasPermission = permissionRepository.CheckPermissionForUser(context.User, requirement.Permission);
if (hasPermission)
{
context.Succeed(requirement);
}
}
}
Y regístralo en tu clase de
Startup
:
services.AddAuthorization(options =>
{
UserDbContext context = ...;
foreach(var permission in context.Permissions)
{
// assuming .Permission is enum
options.AddPolicy(permission.Permission.ToString(),
policy => policy.Requirements.Add(new PermissionRequirement(permission.Permission)));
}
});
// Register it as scope, because it uses Repository that probably uses dbcontext
services.AddScope<IAuthorizationHandler, PermissionHandler>();
Y finalmente en el controlador
[HttpGet]
[Authorize(Policy = PermissionEnum.PERSON_LIST.ToString())]
public ActionResult Index(PersonListQuery query)
{
...
}
La ventaja de esta solución es que también puede tener múltiples controladores para un requisito, es decir, si el primero tiene éxito, el segundo controlador puede determinar que es un error y puede usarlo con autorización basada en recursos con poco esfuerzo adicional.
El enfoque basado en políticas es la forma preferida de hacerlo por el equipo de ASP.NET Core.
De blowdart :
No queremos que escriba atributos de autorización personalizados. Si necesita hacer eso, hemos hecho algo mal. En su lugar, debe escribir los requisitos de autorización.
Tenía el mismo requisito y lo hice como se muestra a continuación y funciona bien para mí. Estoy usando .Net Core 2.0 Webapi
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class CheckAccessAttribute : AuthorizeAttribute, IAuthorizationFilter
{
private string[] _permission;
public CheckAccessAttribute(params string[] permission)
{
_permission = permission;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var user = context.HttpContext.User;
if (!user.Identity.IsAuthenticated)
{
return;
}
IRepository service = (IRepositoryWrapper)context.HttpContext.RequestServices.GetService(typeof(IRepository));
var success = service.CheckAccess(userName, _permission.ToList());
if (!success)
{
context.Result = JsonFormatter.GetErrorJsonObject(CommonResource.error_unauthorized,
StatusCodeEnum.Forbidden);
return;
}
return;
}
}
En el controlador, úsalo como a continuación
[HttpPost]
[CheckAccess(Permission.CreateGroup)]
public JsonResult POST([FromBody]Group group)
{
// your code api code here.
}