ASP.NET Core 认证与授权[7]:动态授权 (2)

基于资源的权限非常简单,但是每次都要在应用代码中显示调用IAuthorizationService,显然比较繁琐,我们也可以使用AOP模式,或者使用EF Core拦截器来实现,将授权验证与业务代码分离。

基于权限的授权

在一个通用的用户权限管理系统中,通常每一个Action都代表一种权限,用户拥有哪些权限也是可以动态分配的。本小节就来介绍一下在 ASP.NET Core 中,如何实现一个简单权限管理系统。

定义权限项

首先,我们要确定我们的系统分为哪些权限项,这通常是由业务所决定的,并且是预先确定的,我们可以硬编码在代码中,方便统一调用:

public static class Permissions { public const string User = "User"; public const string UserCreate = "User.Create"; public const string UserRead = "User.Read"; public const string UserUpdate = "User.Update"; public const string UserDelete = "User.Delete"; }

如上,我们简单定义了“创建用户”,“查询用户”,“更新用户”,“删除用户”四个权限。通常会对权限项进行分组,构成一个树形结构,这样在展示和配置权限时,都会方便很多。在这里,使用.来表示层级进行分组,其中User权限项包含所有以User.开头的权限。

定义权限Requirement

与基于资源的授权类似,我们同样需要定义一个权限Requirement:

public class PermissionAuthorizationRequirement : IAuthorizationRequirement { public PermissionAuthorizationRequirement(string name) { Name = name; } public string Name { get; set; } }

使用Name属性来表示权限的名称,与上面Permissions的常量对应。

实现权限授权Handler

然后实现与上面定义的 Requirement 对应的授权Handler:

public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionAuthorizationRequirement> { private readonly UserStore _userStore; public PermissionAuthorizationHandler(UserStore userStore) { _userStore = userStore; } protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionAuthorizationRequirement requirement) { if (context.User != null) { if (context.User.IsInRole("admin")) { context.Succeed(requirement); } else { var userIdClaim = context.User.FindFirst(_ => _.Type == ClaimTypes.NameIdentifier); if (userIdClaim != null) { if (_userStore.CheckPermission(int.Parse(userIdClaim.Value), requirement.Name)) { context.Succeed(requirement); } } } } return Task.CompletedTask; } }

如上,把admin角色设置为内部固定角色,直接跳过授权检查。其他角色则从Claims中取出用户Id,然后调用CheckPermission完成授权。

权限检查的具体逻辑就属于业务层面的了,通常会从数据库中查找用的的权限列表进行验证,这里就不在多说,简单模拟了一下:

public class UserStore { private static List<User> _users = new List<User>() { new User { Id=1,, Password="111111", Role="admin", Email="admin@gmail.com", PhoneNumber="18800000000"}, new User { Id=2,, Password="111111", Role="user", Email="alice@gmail.com", PhoneNumber="18800000001", Permissions = new List<UserPermission> { new UserPermission { UserId = 1, PermissionName = Permissions.User }, new UserPermission { UserId = 1, PermissionName = Permissions.Role } } }, new User { Id=3,, Password="111111", Role = "user", Email="bob@gmail.com", PhoneNumber="18800000002", Permissions = new List<UserPermission> { new UserPermission { UserId = 2, PermissionName = Permissions.UserRead }, new UserPermission { UserId = 2, PermissionName = Permissions.RoleRead } } }, }; public bool CheckPermission(int userId, string permissionName) { var user = Find(userId); if (user == null) return false; return user.Permissions.Any(p => permissionName.StartsWith(p.PermissionName)); } }

最后,与上面示例一样,将Handler注册到DI系统中:

services.AddSingleton<IAuthorizationHandler, PermissionAuthorizationHandler>(); 使用策略授权

那么,怎么在应用代码中使用基于权限的授权呢?

最为简单的,我们可以直接借助于 ASP.NET Core 的授权策略来实现基于权限的授权,因为此时并不需要资源。

services.AddAuthorization(options => { options.AddPolicy(Permissions.UserCreate, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserCreate))); options.AddPolicy(Permissions.UserRead, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserRead))); options.AddPolicy(Permissions.UserUpdate, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserUpdate))); options.AddPolicy(Permissions.UserDelete, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserDelete))); });

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyxdpy.html