前面二篇中,熟悉了五種受權方式(對於上篇講的策略受權,還有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
參考文獻