当我的客户要求为每个角色提供细粒度权限时,我遇到了类似的问题。我找不到修改 Authorize 属性的方法,但能够使用自定义属性实现解决方案。但这取决于一件事,即你能得到调用用户的 userId 吗?我使用了 cookie 身份验证,所以当有人登录时,我只在我的声明中包含 userId,这样当请求到来时,我总是可以从那里获取它。我认为 asp.net 中的内置 Session 逻辑也可以完成这项工作,但我不能肯定地说。无论如何,自定义授权的逻辑是这样的:
- 在启动时将用户和角色从数据库加载到缓存。如果您没有在程序中设置缓存(并且不想),您可以通过创建一个包含 2 个静态列表的 UserRoleCache 类来简单地创建自己的缓存。还有几种方法可以在启动时从 db 加载数据,但我发现直接在 Program.cs 中很容易做到这一点,如下所示。
- 定义您的自定义属性,通过遍历缓存中的列表来检查调用用户是否具有所需的角色,如果没有则返回 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);
}
}
最后制作角色属性
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()
{
}
}