韶关市开发区建设局网站,官网建设的意义,加强网站建设 实施政务公开,合肥百度推广优化基于资源的授权 有些场景下#xff0c;授权需要依赖于要访问的资源#xff0c;例如#xff1a;每个资源通常会有一个创建者属性#xff0c;我们只允许该资源的创建者才可以对其进行编辑#xff0c;删除等操作#xff0c;这就无法通过[Authorize]特性来指定授权了。因为授… 基于资源的授权 有些场景下授权需要依赖于要访问的资源例如每个资源通常会有一个创建者属性我们只允许该资源的创建者才可以对其进行编辑删除等操作这就无法通过[Authorize]特性来指定授权了。因为授权过滤器会在我们的应用代码以及MVC的模型绑定之前执行无法确定所访问的资源。此时我们需要使用基于资源的授权下面就来演示一下具体是如何操作的。 定义资源Requirement 在基于资源的授权中我们要判断的是用户是否具有针对该资源的某项操作因此我们先定义一个代表操作的Requirement public class MyRequirement : IAuthorizationRequirement{ public string Name { get; set; }
} 可以根据实际场景来定义需要的属性在本示例中只需要一个Name属性用来表示针对资源的操作名称如增查改删等。 然后我们预定义一些常用的操作方便业务中的调用 public static class Operations{ public static MyRequirement Create new MyRequirement { Name Create }; public static MyRequirement Read new MyRequirement { Name Read }; public static MyRequirement Update new MyRequirement { Name Update }; public static MyRequirement Delete new MyRequirement { Name Delete };
} 上面定义的 MyRequirement 虽然很简单但是非常通用因此在 ASP.NET Core 中也内置了一个OperationAuthorizationRequirement public class OperationAuthorizationRequirement : IAuthorizationRequirement{ public string Name { get; set; }
} 在实际应用中我们可以直接使用OperationAuthorizationRequirement而不需要再自定义 Requirement而在这里只是为了方便理解后续也继续使用 MyRequirement 来演示。 实现资源授权Handler 每一个 Requirement 都需要有一个对应的 Handler来完成授权逻辑可以直接让 Requirement 实现IAuthorizationHandler接口也可以单独定义授权Handler在这里使用后者。 在本示例中我们是根据资源的创建者来判断用户是否具有操作权限因此我们定义一个资源创建者的接口而不是直接依赖于具体的资源 public interface IDocument{ string Creator { get; set; }
} 然后实现我们的授权Handler: public class DocumentAuthorizationHandler : AuthorizationHandlerOperationAuthorizationRequirement, IDocument
{ protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, OperationAuthorizationRequirement requirement, IDocument resource) { // 如果是Admin角色就直接授权成功if (context.User.IsInRole(admin)){context.Succeed(requirement);} else{ // 允许任何人创建或读取资源if (requirement Operations.Create || requirement Operations.Read){context.Succeed(requirement);} else{ // 只有资源的创建者才可以修改和删除if (context.User.Identity.Name resource.Creator){context.Succeed(requirement);} else{context.Fail();}}} return Task.CompletedTask;}
} 在前面章节的《自定义策略》示例中我们继承的是AuthorizationHandlerNameAuthorizationRequirement而这里继承了AuthorizationHandlerOperationAuthorizationRequirement, Document很明显比之前的多了resource参数以便用来实现基于资源的授权。 如上我们并没有验证用户是否已登录以及context.User是否为空等。这是因为在 ASP.NET Core 的默认授权中已经对这些进行了判断我们只需要在要授权的控制器上添加[Authorize]特性即可无需重复性的工作。 最后不要忘了还需要将DocumentAuthorizationHandler注册到DI系统中 services.AddSingletonIAuthorizationHandler, DocumentAuthorizationHandler(); 调用AuthorizationService 现在就可以在我们的应用代码中调用IAuthorizationService来完成授权了不过在此之前我们再来回顾一下IAuthorizationService接口 public interface IAuthorizationService{ TaskAuthorizationResult AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerableIAuthorizationRequirement requirements); TaskAuthorizationResult AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName);
} 在《上一章》中我们提到使用[Authorize]设置授权时其AuthorizationHandlerContext中的resource字段被设置为空现在我们将要授权的资源传进去即可 [Authorize]public class DocumentsController : Controller{ public async TaskActionResult Details(int? id) { var document _docStore.Find(id.Value); if (document null){ return NotFound();} if ((await _authorizationService.AuthorizeAsync(User, document, Operations.Read)).Succeeded){ return View(document);} else{ return new ForbidResult();}} public async TaskIActionResult Edit(int? id) { var document _docStore.Find(id.Value); if (document null){ return NotFound();} if ((await _authorizationService.AuthorizeAsync(User, document, Operations.Update)).Succeeded){ return View(document);} else{ return new ForbidReuslt();}}
} 如上在授权失败时我们返回了ForbidResult建议不要返回ChallengeResult因为我们要明确的告诉用户是无权访问而不是未登录。 基于资源的权限非常简单但是每次都要在应用代码中显示调用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 : AuthorizationHandlerPermissionAuthorizationRequirement
{ 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 ListUser _users new ListUser() { new User { Id1, Nameadmin, Password111111, Roleadmin, Emailadmingmail.com, PhoneNumber18800000000}, new User { Id2, Namealice, Password111111, Roleuser, Emailalicegmail.com, PhoneNumber18800000001, Permissions new ListUserPermission { new UserPermission { UserId 1, PermissionName Permissions.User }, new UserPermission { UserId 1, PermissionName Permissions.Role }}}, new User { Id3, Namebob, Password111111, Role user, Emailbobgmail.com, PhoneNumber18800000002, Permissions new ListUserPermission { 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.AddSingletonIAuthorizationHandler, 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)));
}); 如上针对每一个权限项都定义一个对应的授权策略然后就可以在控制器中直接使用[Authorize]来完成授权 [Authorize]public class UserController : Controller{[Authorize(Policy Permissions.UserRead)] public ActionResult Index() {}[Authorize(Policy Permissions.UserRead)] public ActionResult Details(int? id) {}[Authorize(Policy Permissions.UserCreate)] public ActionResult Create() { return View();}[Authorize(Policy Permissions.UserCreate)][HttpPost][ValidateAntiForgeryToken] public IActionResult Create([Bind(Title)] User user) {}
} 当然我们也可以像基于资源的授权那样在应用代码中调用IAuthorizationService完成授权这样做的好处是无需定义策略但是显然一个一个来定义策略太过于繁琐。 还有一种更好方式就是使用MVC过滤器来完成对IAuthorizationService的调用下面就来演示一下。 自定义授权过滤器 我们可以参考上一章中介绍的《AuthorizeFilter》来自定义一个权限过滤器 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple true, Inherited true)]public class PermissionFilter : Attribute, IAsyncAuthorizationFilter{ public PermissionFilter(string name) {Name name;} public string Name { get; set; } public async Task OnAuthorizationAsync(AuthorizationFilterContext context) { var authorizationService context.HttpContext.RequestServices.GetRequiredServiceIAuthorizationService(); var authorizationResult await authorizationService.AuthorizeAsync(context.HttpContext.User, null, new PermissionAuthorizationRequirement(Name)); if (!authorizationResult.Succeeded){context.Result new ForbidResult();}}
} 上面的实现非常简单我们接受一个name参数代表权限的名称然后将权限名称转化为PermissionAuthorizationRequirement最后直接调用 authorizationService 来完成授权。 接下来我们就可以直接在控制器中使用PermissionFilter过滤器来完成基于权限的授权了 [Authorize]public class UserController : Controller{[PermissionFilter(Permissions.UserRead)] public ActionResult Index() { return View(_userStore.GetAll());}[PermissionFilter(Permissions.UserCreate)] public ActionResult Create() {}[PermissionFilter(Permissions.UserCreate)][HttpPost][ValidateAntiForgeryToken] public IActionResult Create([Bind(Title)] User user) {}[PermissionFilter(Permissions.UserUpdate)] public IActionResult Edit(int? id) {}[PermissionFilter(Permissions.UserUpdate)][HttpPost][ValidateAntiForgeryToken] public IActionResult Edit(int id, [Bind(Id,Title)] User user) {}
} 在视图中使用授权 通常在前端页面当中我们也需要根据用户的权限来判断是否显示“添加”“删除”等按钮而不是让用户点击“添加”再提示用户没有权限这在 ASP.NET Core 中实现起来也非常简单。 我们可以直接在Razor视图中注入IAuthorizationService来检查用户权限 inject IAuthorizationService AuthorizationServiceif ((await AuthorizationService.AuthorizeAsync(User, AuthorizationSample.Authorization.Permissions.UserCreate)).Succeeded)
{ pa asp-actionCreate创建/a/p} 不过上面的代码是通过策略名称来授权的如果我们使用了上面创建的授权过滤器而没有定义授权策略的话需要使用如下方式来实现 inject IAuthorizationService AuthorizationServiceif ((await AuthorizationService.AuthorizeAsync(User, new PermissionAuthorizationRequirement(AuthorizationSample.Authorization.Permissions.UserCreate))).Succeeded)
{ pa asp-actionCreate创建/a/p} 我们也可以定义一个AuthorizationService的扩展方法实现通过权限名称进行授权这里就不再多说。 我们不能因为隐藏了操作按钮就不在后端进行授权验证了就像JS的验证一样前端的验证就为了提升用户的体验后端的验证在任何时候都是必不可少的。 总结 在大多数场景下我们只需要使用授权策略就可以应对而在授权策略不能满足我们的需求时由于 ASP.NET Core 提供了一个统一的 IAuthorizationService 授权接口这就使我们扩展起来也非常方便。ASP.NET Core 的授权部分到这来也就介绍完了总的来说要比ASP.NET 4.x的时候简单灵活很多可见 ASP.NET Core 不仅仅是为了跨平台而是为了适应现代应用程序的开发方式而做出的全新的设计我们也应该用全新的思维去学习.NET Core踏上时代的浪潮。 相关文章 ASP.NET Core 认证与授权[4]:JwtBearer认证 ASP.NET Core 认证与授权[2]:Cookie认证 ASP.NET Core 认证与授权[3]:OAuth OpenID Connect认证 Asp.Net Core 2.0 多角色权限认证 asp.net core 2.0 web api基于JWT自定义策略授权 ASP.NET Core 认证与授权[5]:初识授权 ASP.NET Core 认证与授权[6]:授权策略是怎么执行的 原文http://www.cnblogs.com/RainingNight/p/dynamic-authorization-in-asp-net-core.html .NET社区新闻深度好文欢迎访问公众号文章汇总 http://www.csharpkit.com