Cómo implementar el Control de Acceso Basado en Permisos con Asp.Net Básica


Estoy tratando de implementar el control de acceso basado en permisos con aspnet core. Para administrar dinámicamente roles de usuario y permisos(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 custom AuthorizeAttribute 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;
    }
}

Entonces 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.Elementos ["CUSTOM_USER"] en vistas para mostrar u ocultar 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 OnAuthorization en el AuthorizeAttribute. Intenté algunas formas de resolver el problema. Estos son los siguientes:

  • Uso de la nueva autorización basada en políticas(creo que no es adecuado para mi scenerio)

  • Usando custom AuthorizeAttribute y AuthorizationFilter (leí esto post https://stackoverflow.com/a/35863514/5426333 pero yo no podía cambiarlo correctamente)

  • Usando middleware personalizado (cómo obtener AuthorizeAttribute de corriente la acción?)

  • Uso de ActionFilter (¿es correcto por motivos de seguridad?)

No pude decidir qué camino es el mejor para mi scenerio y cómo implementarlo.

First question : Is MVC5 implementation bad practice?

Segunda pregunta: ¿Tiene alguna sugerencia para implementar aspnet core?

Author: Community, 2016-04-06

2 answers

Basado en los comentarios, aquí 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 Startup clase:

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 varios 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.

La política basada enfoque es la forma preferida de hacerlo por el ASP.NET Equipo central.

De blowdart:

No queremos que escribas atributos de autorización personalizados. Si necesitas hacer eso, hemos hecho algo malo. En su lugar, debe escribir los requisitos de autorización.

 35
Author: Tseng,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-16 15:31:33

Para una solución que no requiere que agregue una política para cada permiso, vea mi respuesta para otra pregunta.

Le permite decorar sus Controladores y Acciones con cualquier atributo personalizado que desee, y acceder a ellos en su AuthorizationHandler.

 1
Author: Shawn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:09:42