После долгих проб и ошибок и многих исследований я нашел адекватный ответ: (большое спасибо Mohen 'mo-esmp' Esmailpour)
Создать 2 класса:
public class MvcControllerInfo
{
public string Id => $"{AreaName}:{Name}";
public string Name { get; set; }
public string DisplayName { get; set; }
public string AreaName { get; set; }
public IEnumerable<MvcActionInfo> Actions { get; set; }
}
public class MvcActionInfo
{
public string Id => $"{ControllerId}:{Name}";
public string Name { get; set; }
public string DisplayName { get; set; }
public string ControllerId { get; set; }
}
Добавьте еще один класс MvcControllerDiscovery
в папку Services
, чтобы обнаружить все контроллеры и действия:
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;
}
}
IActionDescriptorCollectionProvider
предоставляет кэшированную коллекцию ActionDescriptor
, каждый дескриптор которой представляет действие. Откройте класс запуска и внутри метода Configure и зарегистрируйте MvcControllerDiscovery
зависимость.
services.AddSingleton<IMvcControllerDiscovery, MvcControllerDiscovery>();
добавить контроллер ролей для управления ролями. В папке Controller
создайте RoleController
, затем добавьте Create
action:
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();
}
}
Создать класс RoleViewModel
в каталоге Models
:
public class RoleViewModel
{
[Required]
[StringLength(256, ErrorMessage = "The {0} must be at least {2} characters long.")]
public string Name { get; set; }
public IEnumerable<MvcControllerInfo> SelectedControllers { get; set; }
}
И в папке View добавьте другую папку и назовите ее Role, затем добавьте представление Create.cshtml. Я использовал jQuery.bonsai для отображения контроллера и иерархии действий.
@model RoleViewModel
@{
ViewData["Title"] = "Create Role";
var controllers = (IEnumerable<MvcControllerInfo>)ViewData["Controllers"];
}
@section Header {
<link href="~/lib/jquery-bonsai/jquery.bonsai.css" rel="stylesheet" />
}
<h2>Create Role</h2>
<hr />
<div class="row">
<div class="col-md-6">
<form asp-action="Create" class="form-horizontal">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label col-md-2"></label>
<div class="col-md-10">
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label">Access List</label>
<div class="col-md-9">
<ol id="tree">
@foreach (var controller in controllers)
{
string name;
{
name = controller.DisplayName ?? controller.Name;
}
<li class="controller" data-value="@controller.Name">
<input type="hidden" class="area" value="@controller.AreaName" />
@name
@if (controller.Actions.Any())
{
<ul>
@foreach (var action in controller.Actions)
{
{
name = action.DisplayName ?? action.Name;
}
<li data-value="@action.Name">@name</li>
}
</ul>
}
</li>
}
</ol>
</div>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script src="~/lib/jquery-qubit/jquery.qubit.js"></script>
<script src="~/lib/jquery-bonsai/jquery.bonsai.js"></script>
<script>
$(function () {
$('#tree').bonsai({
expandAll: false,
checkboxes: true,
createInputs: 'checkbox'
});
$('form').submit(function () {
var i = 0, j = 0;
$('.controller > input[type="checkbox"]:checked, .controller > input[type="checkbox"]:indeterminate').each(function () {
var controller = $(this);
if ($(controller).prop('indeterminate')) {
$(controller).prop("checked", true);
}
var controllerName = 'SelectedControllers[' + i + ']';
$(controller).prop('name', controllerName + '.Name');
var area = $(controller).next().next();
$(area).prop('name', controllerName + '.AreaName');
$('ul > li > input[type="checkbox"]:checked', $(controller).parent()).each(function () {
var action = $(this);
var actionName = controllerName + '.Actions[' + j + '].Name';
$(action).prop('name', actionName);
j++;
});
j = 0;
i++;
});
return true;
});
});
</script>
}
Это должно заставить вас отображать каждое действие во всех контроллерах во внешнем интерфейсе, чтобы настроить доступ разрешений для любой роли.
Если у вас нет класса, унаследованного от идентификационного пользователя, вы можете выполнить остальные шаги по ссылке ниже, чтобы показать, как установить роль для конкретного пользователя. Удачи!
https://github.com/mo-esmp/DynamicRoleBasedAuthorizationNETCore/blob/master/README.md
Надеюсь, это поможет.