Как реализовать авторизацию ролей с настраиваемой базой данных?

У меня есть приложение, для которого требуется авторизация ролей с использованием настраиваемой базы данных. База данных настроена с tblUsers таблицей, которая имеет ссылку на tblRoles таблицу. Пользователи также уже назначены своим ролям.

Я также хочу использовать атрибут [Authorize(Role = "RoleName")] для каждого действия, чтобы проверить, назначен ли аутентифицированный пользователь "RoleName" в базе данных. У меня много проблем с выяснением, где мне нужно внести изменения в атрибут [Authorize], чтобы он вел себя таким образом. Я просто хочу посмотреть, есть ли у имени пользователя роль, у меня не будет страницы для управления ролями в базе данных.

Я пробовал реализовать настраиваемые поставщики хранилищ для ASP.NET Core Identity, но начинает казаться, что это не то, что мне нужно, потому что я не собираюсь управлять ролями в приложении, и я не могу сказать, как это влияет поведение атрибута [Authorize].

Кроме того, вполне вероятно, что я ошибаюсь в своем понимании того, как вообще работает атрибут [Authorize]. Если вы заметили это, я был бы признателен, если бы вы указали на это.


person Lukas    schedule 18.02.2020    source источник


Ответы (1)


У меня была аналогичная проблема, когда мой клиент запрашивал детальные разрешения для каждой роли. Я не смог найти способ изменить атрибут авторизации, но смог реализовать решение с настраиваемым атрибутом. Но это зависит от одного: можете ли вы получить userId вызывающего пользователя? Я использовал аутентификацию с помощью файлов cookie, поэтому я просто включаю userId в свои утверждения, когда кто-то входит в систему, поэтому, когда приходит запрос, я всегда могу получить его оттуда. Я думаю, что встроенная логика сеанса в asp.net тоже может выполнить свою работу, хотя я не могу сказать наверняка. В любом случае логика настраиваемой авторизации выглядит так:

  1. Загружать пользователей и роли из базы данных в кеш при запуске. Если вы не настроили кеш в своей программе (и не хотите), вы можете просто создать свой собственный для этой цели, создав класс UserRoleCache с двумя статическими списками в нем. Также есть несколько способов загрузки данных из базы данных при запуске, но мне было легко сделать это непосредственно в Program.cs, как вы увидите ниже.
  2. Определите свой настраиваемый атрибут, чтобы проверить, имеет ли вызывающий пользователь требуемую роль, перебирая списки в кеше и возвращая 403, если нет.

Измените свой класс Program, например:

    public class Program
    {
        public static async Task Main(string[] args)
        {
            IWebHost webHost = CreateWebHostBuilder(args).Build();

            using (var scope = webHost.Services.CreateScope())
            {
                //Get the DbContext instance. Replace MyDbContext with the 
                //actual name of the context in your program
                var context = scope.ServiceProvider.GetRequiredService<MyDbContext>();

                List<User> users = await context.User.ToListAsync();
                List<Role> roles = await context.Role.ToListAsync();

                //You may make getters and setters, this is just to give you an idea
                UserRoleCache.users = users;
                UserRoleCache.roles = roles;

            }

            webHost.Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();

    }

Затем следует логика проверки наличия у пользователя роли. Обратите внимание, что я использовал множество ролей, потому что иногда вам нужно разрешить доступ к нескольким ролям.

    public class RoleRequirementFilter : IAuthorizationFilter
    {
        private readonly string[] _roles;

        public PermissionRequirementFilter(string[] roles)
        {
            _roles = roles;
        }

        public void OnAuthorization(AuthorizationFilterContext context)
        {
            bool hasRole = false;

            //Assuming there's a way you can get the userId
            var userId = GetUserId();

            User user = UserRoleCache.users.FirstOrDefault(x => x.Id == userId);
            //Where roleType is the name of the role like Admin, Manager etc
            List<Role> roles = UserRoleCache.roles.FindAll(x => _roles.Contains(x.RoleType))

            foreach(var role in roles)
            {
                if(user.RoleId == role.Id)
                {
                    hasRole = true;
                    break;
                }
            }

            if (!hasRole)
                context.Result = new StatusCodeResult(403);
        }
    }

Наконец, сделайте атрибут Role

    public class RoleAttribute : TypeFilterAttribute
    {
        public RoleAttribute(params string[] roles) : base(typeof(RoleRequirementFilter))
        {
            Arguments = new object[] { roles };
        }
    }

Теперь вы можете использовать атрибут Role в своих контроллерах:

public class SampleController : ControllerBase
    {

        [HttpGet]
        [Role("Admin", "Manager")]
        public async Task<ActionResult> Get()
        {

        }

        [HttpPost]
        [Role("Admin")]
        public async Task<ActionResult> Post()
        {

        }
    }
person Nikhil Satiani    schedule 19.02.2020