asp.net core系列 51 Identity 授權(下)


1.6 基於資源的授權

  前面二篇中,熟悉了五種授權方式(對於上篇講的策略授權,還有IAuthorizationPolicyProvider的自定義授權策略提供程序沒有講,后面再補充)。本篇講的授權方式不是一種全新的授權方式,而是授權應用場景的靈活控制。

  基於資源的授權是控制在 razor pages處理程序或mvc的action之中。資源:比如作者發表的文章,只有該作者才能更新文章,文章在進行授權評估之前,必須從數據存儲中檢索文章。

 

  (1) 引用 IAuthorizationService 授權服務

    授權作為實現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) 授權需求定義

    基於 CRUD (創建、 讀取、 更新、 刪除) 的授權操作,使用OperationAuthorizationRequirement幫助器類,來提供一些授權名稱。

    /// <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) 定義處理程序

    /// <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驗證授權

    當用戶登錄后,要訪問該文章頁面時(/Document/index/1),使用AuthorizeAsync方法進行調用,確定當前用戶是否允許查看提供的文章.

        /// <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 >

    

1.7 基於視圖的授權    

   在項目開發中,授權權限還需要控制頁面,對頁面的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

 

  參考文獻

    基於資源的授權

    基於視圖的授權


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM