.NETCore中的OverrideAuthorization属性

时间:2017-11-30 10:42:30

标签: asp.net-core-mvc .net-core asp.net-core-2.0

在下面的控制器代码中,只有位于"管理员"由于控制器级GetData(),角色可以访问AuthorizeAttribute操作方法。但我也想要那些只在"经理"有权访问GetData()操作方法的角色。

[Authorize(Roles = "Administrator")]
Public class AdminController : Controller
{
    [Authorize(Roles = "Administrator, Manager")]
    public IActionResult GetData()
    {
    }
}

.NET Core框架中是否有像OverrideAuthorization属性这样的选项来实现此要求?

5 个答案:

答案 0 :(得分:2)

在ASP.NET Core 2.1中,您可以执行此操作。检查以下内容:https://docs.microsoft.com/en-us/aspnet/core/security/authorization/roles?view=aspnetcore-2.1

  

您还可以锁定控制器,但允许匿名,   未经授权的对单个操作的访问。

[Authorize(Roles = "Admin,Employee")] // admin or employee
public class XController : Controller 
{
    [Authorize(Roles = "Admin")] // only admin
    public ActionResult ActionX() { ... }

    [AllowAnonymous] // anyone
    public ActionResult ActionX() { ... }
}

答案 1 :(得分:1)

理想情况下,您希望将限制范围缩小到Action Method,因为在Controller Initialization步骤中,它会在Action过滤之前先检查Controller的授权过滤器。

[Authorize(Roles = "Administrator, Manager")]
Public class AdminController : Controller
{
    public IActionResult GetData()
    {
    }

    [Authorize(Roles = "Administrator")]
    public IActionResult AdministratorOnly()
    {
    }
}

答案 2 :(得分:1)

经过长时间的授权程序集分析后,能够找到解决方案。

在startup.cs文件中,按如下方式添加授权:

services.AddAuthorization(options =>
        {
            var roles = new List<string>{ Role.Administrator, Role.Manager};

            var requirement =
                new List<IAuthorizationRequirement> {new AdminManagerAuthorizationOverrideOthers(roles) };
            var sharedAuthentication =
                new AuthorizationPolicy(requirement,
                    new List<string>());
            options.AddPolicy(name: "AdminManager", policy: sharedAuthentication);
            options.AddPolicy(name: "Administrator", configurePolicy: policy => policy.RequireAssertion(e =>
            {
                if (e.Resource is AuthorizationFilterContext afc)
                {
                    var noPolicy = afc.Filters.OfType<AuthorizeFilter>().Any(p =>
                        p.Policy.Requirements.Count == 1 &&
                        p.Policy.Requirements.Single() is AdminManagerAuthorizationOverrideOthers);
                    if (noPolicy)
                        return true;
                }
                return e.User.IsInRole(Role.Administrator);
            }));

        });

在任何名称空间中创建一个类,从“Microsoft.AspNetCore.Authorization.Infrastructure”命名空间继承“RolesAuthorizationRequirement”,如下所示:

public class AdminManagerAuthorizationOverrideOthers : RolesAuthorizationRequirement
{
    public AdminManagerAuthorizationOverrideOthers(IEnumerable<string> allowedRoles) : base(allowedRoles)
    {
    }
}

然后,按如下方式装饰控制器和动作方法:

[Authorize(Policy = "Administrator")]
Public class AdminController : Controller
{
    public IActionResult GetData()
    {
    }

    [Authorize(Policy = "AdminManager")]
    public IActionResult AdministratorOnly()
    {
    }
}

答案 3 :(得分:0)

以上都是正确的,我只想举一个简单的例子 我的情况是Asp.Net Core 3.1

Startup.js(ConfigureServices):

            services.AddIdentity<ApplicationUser, IdentityRole>(config =>
            {
                config.User.RequireUniqueEmail = false;    // óíèêàëüíûé email
                config.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 -._@+"; 
                config.SignIn.RequireConfirmedEmail = false;
            })
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddUserManager<UserManager<ApplicationUser>>()
                .AddRoleManager<RoleManager<IdentityRole>>()
                .AddDefaultTokenProviders();

            services.AddAuthorization(options =>
            {
                options.AddPolicy("User", policy => {
                    policy.RequireClaim("User");
                });
                options.AddPolicy("Admin", policy => {
                    policy.RequireRole("Admin");
                });
            });

services.AddScoped<IAuthorizationHandler, RolesAuthorizationHandler>();

Startup.js(配置):

    app.UseAuthentication();
    app.UseAuthorization();

控制器:

[Authorize(Policy = "Admin")]
public class RoleController : Controller

处理程序示例:

 public class RolesAuthorizationHandler : AuthorizationHandler<RolesAuthorizationRequirement>, IAuthorizationHandler
    {

        private readonly RoleManager<IdentityRole> _roleManager;
        private readonly UserManager<ApplicationUser> _userManager;
        public RolesAuthorizationHandler(RoleManager<IdentityRole> roleManager, UserManager<ApplicationUser> userManager)
        {
            _roleManager = roleManager;
            _userManager = userManager;
        }

        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                       RolesAuthorizationRequirement requirement)
        {
            if (context.User == null || !context.User.Identity.IsAuthenticated)
            {
                context.Fail();
                return Task.CompletedTask;
            }

            var validRole = false;
            if (requirement.AllowedRoles == null ||
                requirement.AllowedRoles.Any() == false)
            {
                validRole = true;
            }
            else
            {
                var claims = context.User.Claims;
                //var userName = claims.FirstOrDefault(c => c.Type == "UserName").Value;
                var allowedRoles = requirement.AllowedRoles;

                var loggedInUserTask = _userManager.GetUserAsync(context.User);
                loggedInUserTask.Wait();
                var user = loggedInUserTask.Result;
                var roles = _userManager.GetRolesAsync(user);
                roles.Wait();
                var roleList = roles.Result;

                validRole = roleList.Where(p => allowedRoles.Contains(p.ToString())).Any();
            }

            if (validRole)
            {
                context.Succeed(requirement);
            }
            else
            {
                context.Fail();
            }
            return Task.CompletedTask;
        }
    }

I在更新曾经存在的项目时,我将旧的用户表移至新的身份数据库中的用户表。后来,我在表级别为角色定义了角色,并通过以这种方式编写的RoleManager,将他的下一个管理权留给了该步骤。非常成功。以我为例,许多人可能会更新他们的旧项目。但是,我没有这样的职位,并想分享。 以下部分适用于他们:

public class RoleAssignViewModel
    {
        public string RoleId { get; set; }
        public string RoleName { get; set; }
        public bool HasAssign { get; set; }
    }

    public class RoleViewModel
    {
        [Required(ErrorMessage = "Fill the role.")]
        [Display(Name = "Role Name")]
        public string Name { get; set; }
    }

    [Authorize(Policy = "Admin")]
    public class RoleController : Controller
    {
        private readonly RoleManager<IdentityRole> _roleManager;
        private readonly UserManager<ApplicationUser> _userManager;
        public RoleController(RoleManager<IdentityRole> roleManager, UserManager<ApplicationUser> userManager)
        {
            _roleManager = roleManager;
            _userManager = userManager;
        }
        public async Task<IActionResult> RoleAssign(string id)
        {
            ApplicationUser user = await _userManager.FindByIdAsync(id);
            List<IdentityRole> allRoles = _roleManager.Roles.ToList();
            List<string> userRoles = await _userManager.GetRolesAsync(user) as List<string>;
            List<RoleAssignViewModel> assignRoles = new List<RoleAssignViewModel>();
            allRoles.ForEach(role => assignRoles.Add(new RoleAssignViewModel
            {
                HasAssign = userRoles.Contains(role.Name),
                RoleId = role.Id,
                RoleName = role.Name
            }));

            return View(assignRoles);
        }
        [HttpPost]
        public async Task<ActionResult> RoleAssign(List<RoleAssignViewModel> modelList, string id)
        {
            ApplicationUser user = await _userManager.FindByIdAsync(id);
            foreach (RoleAssignViewModel role in modelList)
            {
                if (role.HasAssign)
                    await _userManager.AddToRoleAsync(user, role.RoleName);
                else
                    await _userManager.RemoveFromRoleAsync(user, role.RoleName);
            }
            return RedirectToAction("Index", "User");
        }
        public IActionResult RoleList()
        {
            return View(_roleManager.Roles.ToList());
        }
        public async Task<IActionResult> DeleteRole(string id)
        {
            IdentityRole role = await _roleManager.FindByIdAsync(id);
            IdentityResult result = await _roleManager.DeleteAsync(role);
            if (result.Succeeded)
            {
                //Başarılı...
            }
            return RedirectToAction("Index");
        }
        public async Task<IActionResult> CreateRole(string id)
        {
            if (id != null)
            {
                IdentityRole role = await _roleManager.FindByIdAsync(id);

                return View(new RoleViewModel
                {
                    Name = role.Name
                });
            }
            return View();
        }
        [HttpPost]
        public async Task<IActionResult> CreateRole(RoleViewModel model, string id)
        {
            IdentityResult result = null;
            if (id != null)
            {
                IdentityRole role = await _roleManager.FindByIdAsync(id);
                role.Name = model.Name;
                result = await _roleManager.UpdateAsync(role);
            }
            else
                result = await _roleManager.CreateAsync(new IdentityRole { Name = model.Name });

            if (result.Succeeded)
            {
                //Başarılı...
            }
            return View();
        }

        //[Authorize]
        public IActionResult UserRoleList()
        {
            return View(_userManager.Users);
        }

    }

答案 4 :(得分:0)

在这里找到我正在使用的东西:https://github.com/dotnet/aspnetcore/issues/8149#issuecomment-471927034

/// <summary>
/// https://github.com/dotnet/aspnetcore/issues/8149#issuecomment-471927034
/// </summary>
public class OverrideFilter : ActionFilterAttribute
{
    public Type Type { get; set; }
}

public class OverrideFilterProvider : IFilterProvider
{
    public int Order => 1;

    public void OnProvidersExecuted(FilterProviderContext context) { }

    public void OnProvidersExecuting(FilterProviderContext context)
    {
        if (context.ActionContext.ActionDescriptor.FilterDescriptors != null)
        {
            //Check whether the method has any OverrideFilter
            var overrideFilters = context.Results.Where(filterItem => filterItem.Filter is OverrideFilter).ToList();
            foreach (var overrideFilter in overrideFilters)
            {
                //Remove the filters of the corresponding type, but with smaller scope
                context.Results.RemoveAll(filterItem =>
                    filterItem.Descriptor.Filter.GetType() == ((OverrideFilter)overrideFilter.Filter).Type &&
                    filterItem.Descriptor.Scope < overrideFilter.Descriptor.Scope);
            }
        }
    }
}

public class OverrideAuthorization : OverrideFilter
{
    public OverrideAuthorization()
    {
        Type = typeof(AuthorizeFilter);
    }
}

/// <summary>
/// https://stackoverflow.com/questions/16606281/linq-to-remove-certain-elements-from-a-ilistt-based-on-a-ilistint
/// </summary>
public static class IListExt
{
    public static int RemoveAll<T>(this IList<T> list, Predicate<T> match)
    {
        int count = 0;

        for (int i = list.Count - 1; i >= 0; i--)
        {
            if (match(list[i]))
            {
                ++count;
                list.RemoveAt(i);
            }
        }

        return count;
    }
}

最后我们按如下方式注入它(我不确定这是注入它的正确方法,但它可以起作用);

services.TryAddEnumerable(ServiceDescriptor.Singleton<IFilterProvider, OverrideFilterProvider>());

使用方式

[Authorize(Policy = "ControllerPolicy")
public class MyController : Controller
{
    [OverrideAuthorization]
    [Authorize(Policy = "ActionPolicy")]
    public IActionResult MyAction()
    {
        //Only ActionPolicy will be applied, while ControllerPolicy will be ignored
    }
}