在开发ASP.NET Core应用程序时,经常需要对用户进行权限控制,以确保只有具备相应权限的用户才能访问特定的控制器或操作。本文将介绍如何实现一个自定义的权限控制机制,以便在ASP.NET Core应用程序中使用简单的属性来控制对控制器或动作方法的访问。
假设已经有一个自行实现的权限管理系统,并且希望在ASP.NET Core应用程序中使用简单的属性来设置控制器或动作方法所需的权限。例如,可能希望使用类似于ASP.NET 4.5中的[Authorize(Roles="Administrator")]这样的属性来控制访问。
首先,需要从数据库或其他存储介质中获取用户的权限信息。在本文的例子中,将只展示IUserCache接口的实现,该接口实现了用户的缓存存储库。这些用户具有一个属性,即权限列表。当缓存时间过期或缓存中不存在该用户时,UserCache将重新加载用户。
为了实现自定义权限控制,需要添加以下Nuget包:
创建一个从ClaimsPrincipal派生的Principal类。这个类重写了IsInRole方法,使用PermissionProvider来检查角色。Principal需要PermissionProvider,它将检查用户的Roles。
public class AppPrincipal : ClaimsPrincipal {
private readonly IPermissionProvider _PermissionProvider;
public AppPrincipal(IPermissionProvider permissionProvider, IIdentity ntIdentity) : base((ClaimsIdentity)ntIdentity) {
_PermissionProvider = permissionProvider;
}
public override bool IsInRole(string role) {
return _PermissionProvider.IsUserAuthorized(this, role);
}
}
需要一个权限提供者,它能够根据用户的身份来获取权限。
public class PermissionProvider : IPermissionProvider {
private readonly IServerConfiguration _ServerConfiguration;
private readonly IUserCache _UserCache;
private readonly string _AdministratorPermission;
public PermissionProvider(IServerConfiguration serverConfiguration, IUserCache userCache) {
_ServerConfiguration = serverConfiguration;
_UserCache = userCache;
_AdministratorPermission = _ServerConfiguration.AdministratorPermissionName;
}
public bool IsUserAuthorized(IPrincipal principal, string permission) {
if (string.IsNullOrWhiteSpace(permission)) return true;
var user = _UserCache.GetUserFromPrincipal(principal);
if (user == null) return false;
// User has Permission and Permission is Valid
if (user.ApplicationPermissions.Any(i => i.IsValid && i.Permission.Equals(permission, StringComparison.OrdinalIgnoreCase))) {
return true;
}
// User has Administrator Permission and Permission is Valid
if (user.ApplicationPermissions.Any(i => i.IsValid && i.Permission.Equals(_AdministratorPermission, StringComparison.OrdinalIgnoreCase))) {
return true;
}
return false;
}
}
需要一个自定义属性来执行权限检查。
public class RequiresPermissionAttribute : TypeFilterAttribute {
public RequiresPermissionAttribute(params string[] permissions) : base(typeof(RequiresPermissionAttributeExecutor)) {
Arguments = new[] { new PermissionAuthorizationRequirement(permissions) };
}
private class RequiresPermissionAttributeExecutor : Attribute, IAsyncResourceFilter {
private readonly ILogger _logger;
private readonly PermissionAuthorizationRequirement _requiredPermissions;
private readonly IPermissionProvider _PermissionProvider;
public RequiresPermissionAttributeExecutor(ILogger logger, PermissionAuthorizationRequirement requiredPermissions, IPermissionProvider permissionProvider) {
_logger = logger;
_requiredPermissions = requiredPermissions;
_PermissionProvider = permissionProvider;
}
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) {
var principal = new AppPrincipal(_PermissionProvider, context.HttpContext.User.Identity);
bool isInOneOfThisRole = false;
foreach (var item in _requiredPermissions.RequiredPermissions) {
if (principal.IsInRole(item)) {
isInOneOfThisRole = true;
}
}
if (!isInOneOfThisRole) {
context.Result = new UnauthorizedResult();
await context.Result.ExecuteResultAsync(context);
} else {
await next();
}
}
}
}
授权要求表示在属性中设置的参数。
public class PermissionAuthorizationRequirement : IAuthorizationRequirement {
public IEnumerable RequiredPermissions { get; }
public PermissionAuthorizationRequirement(IEnumerable requiredPermissions) {
RequiredPermissions = requiredPermissions;
}
}
现在需要在Startup.cs类中设置依赖注入的配置。
public void ConfigureServices(IServiceCollection services) {
// Add framework services.
services.AddMvc();
services.AddSingleton();
services.AddSingleton();
services.AddTransient();
}
以下是控制器中的示例:
[RequiresPermission(Permission.User)]
public class UserAccountController : Controller {
public async Task Get() {
try {
return Ok();
} catch (Exception e) {
return BadRequest(e);
}
}
[RequiresPermission(Permission.Manager)]
public async Task GetManager() {
try {
return Ok();
} catch (Exception e) {
return BadRequest(e);
}
}
}