I have custom user, role, userstore, rolestore. I can do authentication operations. But i want dynamic authorization. Adding new role and relation role and permissions and set this role to user. I want something like that https://imgur.com/jgl5xrs
Users model
namespace App.Models
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
[Table("AppUsers")]
public class User
{
[Key, Required]
public Guid Id { get; set; }
[Required, MaxLength(128)]
public string UserName { get; set; }
[Required, MaxLength(1024)]
public string PasswordHash { get; set; }
[Required, MaxLength(128)]
public string Email { get; set; }
[MaxLength(32)]
public string EmployeeName { get; set; }
public virtual ICollection<UserRole> UserRoles { get; set; }
}
}
Role models
namespace App.Models
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
[Table("Role")]
public class Role
{
[Key, Required]
public Guid Id { get; set; }
[Required]
public string RoleName { get; set; }
public virtual ICollection<UserRole> UserRoles { get; set; }
}
}
UserStore
namespace App.Identity
{
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using App.Data;
using App.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore.Extensions.Internal;
public class UserStore : IUserStore<User>, IUserPasswordStore<User>
{
...
}
}
When you are creating a new role, you need to discover all controllers and their actions and choose role can access which controllers and actions.
public class MvcControllerDiscovery : IMvcControllerDiscovery
{
private List<MvcControllerInfo> _mvcControllers;
private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider;
public MvcControllerDiscovery(IActionDescriptorCollectionProvider actionDescriptorCollectionProvider)
{
_actionDescriptorCollectionProvider = actionDescriptorCollectionProvider;
}
public IEnumerable<MvcControllerInfo> GetControllers()
{
if (_mvcControllers != null)
return _mvcControllers;
_mvcControllers = new List<MvcControllerInfo>();
var items = _actionDescriptorCollectionProvider
.ActionDescriptors.Items
.Where(descriptor => descriptor.GetType() == typeof(ControllerActionDescriptor))
.Select(descriptor => (ControllerActionDescriptor)descriptor)
.GroupBy(descriptor => descriptor.ControllerTypeInfo.FullName)
.ToList();
foreach (var actionDescriptors in items)
{
if (!actionDescriptors.Any())
continue;
var actionDescriptor = actionDescriptors.First();
var controllerTypeInfo = actionDescriptor.ControllerTypeInfo;
var currentController = new MvcControllerInfo
{
AreaName = controllerTypeInfo.GetCustomAttribute<AreaAttribute>()?.RouteValue,
DisplayName = controllerTypeInfo.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName,
Name = actionDescriptor.ControllerName,
};
var actions = new List<MvcActionInfo>();
foreach (var descriptor in actionDescriptors.GroupBy(a => a.ActionName).Select(g => g.First()))
{
var methodInfo = descriptor.MethodInfo;
actions.Add(new MvcActionInfo
{
ControllerId = currentController.Id,
Name = descriptor.ActionName,
DisplayName = methodInfo.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName,
});
}
currentController.Actions = actions;
_mvcControllers.Add(currentController);
}
return _mvcControllers;
}
}
in RoleController
and Create
action return list of all controllers:
public class RoleController : Controller
{
private readonly IMvcControllerDiscovery _mvcControllerDiscovery;
public RoleController(IMvcControllerDiscovery mvcControllerDiscovery)
{
_mvcControllerDiscovery = mvcControllerDiscovery;
}
// GET: Role/Create
public ActionResult Create()
{
ViewData["Controllers"] = _mvcControllerDiscovery.GetControllers();
return View();
}
}
Create role page will be something like this:
For discovering razor pages read Discovering controller actions and Razor Pages in ASP.NET MVC Core
After creating role, assign roles to user and implement custom authorization filter to check weather user can access requested action and controller:
public class DynamicAuthorizationFilter : IAsyncAuthorizationFilter
{
private readonly ApplicationDbContext _dbContext;
public DynamicAuthorizationFilter(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
if (!IsProtectedAction(context))
return;
if (!IsUserAuthenticated(context))
{
context.Result = new UnauthorizedResult();
return;
}
var actionId = GetActionId(context);
var userName = context.HttpContext.User.Identity.Name;
var roles = await (
from user in _dbContext.Users
join userRole in _dbContext.UserRoles on user.Id equals userRole.UserId
join role in _dbContext.Roles on userRole.RoleId equals role.Id
where user.UserName == userName
select role
).ToListAsync();
foreach (var role in roles)
{
var accessList = JsonConvert.DeserializeObject<IEnumerable<MvcControllerInfo>>(role.Access);
if (accessList.SelectMany(c => c.Actions).Any(a => a.Id == actionId))
return;
}
context.Result = new ForbidResult();
}
I have implement dynamic role-based authorization in this way. for full detail take look at Dynamic Role-Based Authorization in ASP.NET Core 2.0 github repo.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.