前面二篇中,熟悉了五种受权方式(对于上篇讲的策略受权,还有IAuthorizationPolicyProvider的自定义受权策略提供程序没有讲,后面再补充)。本篇讲的受权方式不是一种全新的受权方式,而是受权应用场景的灵活控制。html
基于资源的受权是控制在 razor pages处理程序或mvc的action之中。资源:好比做者发表的文章,只有该做者才能更新文章,文章在进行受权评估以前,必须从数据存储中检索文章。git
(1) 引用 IAuthorizationService 受权服务github
受权做为实现IAuthorizationService服务并注册到服务集合的Startup类。 下面在mvc action中引用该接口,准备进行受权控制。数据库
public class DocumentController : Controller { private readonly IAuthorizationService _authorizationService; private readonly IDocumentRepository _documentRepository; public DocumentController(IAuthorizationService authorizationService, IDocumentRepository documentRepository) { _authorizationService = authorizationService; _documentRepository = documentRepository; } }
IAuthorizationService接口有二个AuthorizeAsync
方法重载:安全
//重载1:指定资源resource和策略需求列表 Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements); //重载2:指定资源resource和策略名称 Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName);
(2) 受权需求定义mvc
基于 CRUD (建立、 读取、 更新、 删除) 的受权操做,使用OperationAuthorizationRequirement帮助器类,来提供一些受权名称。async
/// <summary> ///受权四种需求Crud /// </summary> public static class Operations { public static OperationAuthorizationRequirement Create = new OperationAuthorizationRequirement { Name = nameof(Create) }; public static OperationAuthorizationRequirement Read = new OperationAuthorizationRequirement { Name = nameof(Read) }; public static OperationAuthorizationRequirement Update = new OperationAuthorizationRequirement { Name = nameof(Update) }; public static OperationAuthorizationRequirement Delete = new OperationAuthorizationRequirement { Name = nameof(Delete) }; }
(3) 定义处理程序ide
/// <summary> /// 接口AuthorizationHandler<TRequirement, TResource> /// 使用OperationAuthorizationRequirement需求和Document资源 /// </summary> public class DocumentAuthorizationCrudHandler: AuthorizationHandler<OperationAuthorizationRequirement, Document> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, OperationAuthorizationRequirement requirement, Document resource) { //登陆的当前用户是该文章做者,而且有读取权限。实际开发中从数据库读取TResource资源和requirement需求(需求这里是CRUD权限) //动态获取时,能够基于用户声明表UserClaim,也能够基于角色声明表RoleClaim,使用context.User.HasClaim 来判断 if (context.User.Identity?.Name == resource.Author && requirement.Name == Operations.Read.Name) { context.Succeed(requirement); } return Task.CompletedTask; } }
(4) Action中使用AuthorizeAsync验证受权ui
当用户登陆后,要访问该文章页面时(/Document/index/1),使用AuthorizeAsync方法进行调用,肯定当前用户是否容许查看提供的文章.spa
/// <summary> /// /Document/index/1 /// </summary> /// <param name="documentId"></param> /// <returns></returns> public async Task<IActionResult> Index(int documentId) { Document Document = _documentRepository.Find(documentId); if (Document == null) { return new NotFoundResult(); } //使用AuthorizeAsync重载方法(1), 来验证用户访问资源权限,条件是当前用户必需是924964690@qq.com,由于是该用户的文章 var authorizationResult = await _authorizationService.AuthorizeAsync(User, Document, Operations.Read); //若是受权成功,则返回查看文档的页面 if (authorizationResult.Succeeded) { return View(); } //用户已经过身份验证,但受权失败 else if (User.Identity.IsAuthenticated) { return new ForbidResult(); } else { //Challenge:怀疑,返回从新执行身份认证,重定向到登陆页 return new ChallengeResult(); } }
(5) Document实体的定义和该实体仓储
public class Document { public string Author { get; set; } public byte[] Content { get; set; } public int ID { get; set; } public string Title { get; set; } }
public class DocumentRepository : IDocumentRepository { public Document Find(int documentId) { return new Document { Author = "924964690@qq.com", Content = null, ID = documentId, Title = "Test Document" }; } } public interface IDocumentRepository { Document Find(int documentId); }
(6) 添加路由规则,和注入IAuthorizationService服务
services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationCrudHandler>();
routes.MapRoute( name: "document", template: "{controller=Document}/{action=Index}/{documentId?}");
最后当924964690@qq.com用户登陆成功后,访问Document/index/1,查看该文章成功。
总结:基于资源的受权,是应用在mvc的action 中或razor pages处理程序中,是区别以前的几种受权方式, 由于以前讲的受权是:启动程序时受权文件或文件夹,在控制器 action 和PageModel之上应用[Authorize]特性。
对于AuthorizeAsync重载方法(2)的使用案例查看官网文档,这里不在介绍。
思考:在实际开发项目中,处理资源如(增、删、改、查)权限,能够考虑本篇的基于资源的受权,但上面的示例须要改进,由于示例中定义的处理程序只针对Document资源,以及需求(指权限)是写死在处理程序中。若是要实现通用的资源受权,资源和需求权限须要从数据库中获取。例如考虑以下修改:
//定义通用的TResource public class AuthorizationResource { public string UrlResource{get;set;} }
//在index的action中修改 .AuthorizeAsync(User, new AuthorizationResource (){UrlResource="/Document/index/1" }, Operations.Read);
//处理程序修改,省略了受权逻辑处理(数据库获取需求和资源) public class DocumentAuthorizationCrudHandler: AuthorizationHandler<OperationAuthorizationRequirement, AuthorizationResource >
在项目开发中,受权权限还须要控制页面,对页面的html进行显示或隐藏。须要在页面上使用受权服务依赖关系注入,若要将受权服务注入到 Razor 视图中,使用@inject指令。若是但愿每一个视图都能使用受权服务,须要将@inject指令插入 _ViewImports.cshtml的文件视图中。下面的视图受权控制是基于资源的受权。
@using Microsoft.AspNetCore.Authorization
@inject IAuthorizationService AuthorizationService
<!-- 指定策略名称 !--> @if ((await AuthorizationService.AuthorizeAsync(User, "PolicyName")).Succeeded) { <p>This paragraph is displayed because you fulfilled PolicyName.</p> }
<!-- Model是指TResource !--> @if ((await AuthorizationService.AuthorizeAsync(User, Model, Operations.Edit)).Succeeded) { <p><a class="btn btn-default" role="button" href="@Url.Action("Edit", "Document", new { id = Model.Id })">Edit</a></p> }
总结:视图中受权控制不能保证权限安全,还须要在action中实现受权服务。开源Github
参考文献