Создание динамической авторизации на основе ролей - PullRequest
0 голосов
/ 26 июня 2018

Я реализовал авторизацию на основе ролей идентификации, но мне пришлось вручную переходить к каждому контроллеру / действию и указывать индивидуально [Authorize(Roles = "")] очень плохую расширяемость.

Как я могу создать экран пользовательского интерфейса с динамической авторизацией на основе ролей, где «супер-администратор» может настроить, какая роль имеет доступ к контроллеру / действию?

Примерно так:

Portal: Role function

1 Ответ

0 голосов
/ 06 июля 2018

После долгих проб и ошибок и многих исследований я нашел адекватный ответ: (большое спасибо 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

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...