.NET 雲原生架構師訓練營(許可權系統 程式碼實現 EntityAccess)–學習筆記

目錄

  • 開發任務
  • 程式碼實現

開發任務

  • DotNetNB.Security.Core:定義 core,models,Istore;實現 default memory store
  • DotNetNB.Security.EntityAccess:掃描 entities;添加 ef savechanges interceptor

程式碼實現

我們現在已經通過 ActionResourceProvider 完成了 action 的掃描,生成了 ResourceModel,需要持久化到 IResourceStore,持久化之後才可以將它們綁定到用戶,角色

由於 ActionAccess 是一個類庫,提供了一些比較零散的功能,所以需要添加一個擴展方法把功能組裝起來,在 host 啟動的時候執行 action 的掃描

using Microsoft.Extensions.DependencyInjection;

namespace DotNetNB.Security.Core.Extensions
{
    public static class ServiceCollectionExtensions
    {
        public static IServiceCollection AddSecurity(this IServiceCollection services)
        {
            services.AddHostedService<ResourceProviderHostedService>();
            return services;
        }
    }
}

ResourceProviderHostedService 繼承自 IHostedService,有一個 StartAsync 和一個 StopAsync 方法

using Microsoft.Extensions.Hosting;

namespace DotNetNB.Security.Core
{
    public class ResourceProviderHostedService : IHostedService
    {
        public async Task StartAsync(CancellationToken cancellationToken)
        {

        }

        public async Task StopAsync(CancellationToken cancellationToken)
        {

        }
    }
}

新建一個示例的 api 項目 DotNetNB.WebApplication,在這個 api 項目裡面使用我們的 dll 要足夠簡單,就像使用 asp .net core 的 api 一樣

添加 DotNetNB.Security.Core 的項目引用之後,可以直接在 Program.cs 中調用擴展方法

using DotNetNB.Security.Core.Extensions;

...

builder.Services.AddSecurity();

在啟動掃描的時候,Security.Core 並不知道外部的 host 裡面有哪些 action provider,所以需要註冊進來,需要構建一個 builder

同時需要一個配置 options 告訴我們它是來自哪個包,是 ActionAccess,還是 EntityAccess

參照 MvcOptions

builder.Services.AddControllers(options => {});

它是一個 Action 的委託

public static IMvcBuilder AddControllers(
  this IServiceCollection services,
  Action<MvcOptions>? configure)
{
  IMvcCoreBuilder builder = services != null ? MvcServiceCollectionExtensions.AddControllersCore(services) : throw new ArgumentNullException(nameof (services));
  if (configure != null)
    builder.AddMvcOptions(configure);
  return (IMvcBuilder) new MvcBuilder(builder.Services, builder.PartManager);
}

於是乎我們在 AddSecurity 添加一個入參

public static IServiceCollection AddSecurity(this IServiceCollection services, Action<SecurityOption>? configure)

SecurityOption

using Microsoft.Extensions.DependencyInjection;

namespace DotNetNB.Security.Core.Extensions
{
    public class SecurityOption
    {
        public IServiceCollection Services { get; set; }
    }
}

在調用 AddSecurity 擴展方法的時候通過 SecurityOption 進行配置,這樣所有對外的 api 只需要做這一個配置就可以把兩個包的所有功能引用進去

builder.Services.AddSecurity(options =>
{
    options.AddActionAccess();
    options.AddEntityAccess<DBContext>();
});

參考 MvcCoreServiceCollectionExtensions 的 AddMvcCoreServices 方法

internal static void AddMvcCoreServices(IServiceCollection services)
{
    //
    // Options
    //
    services.TryAddEnumerable(
        ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, MvcCoreMvcOptionsSetup>());
    
    ...
}

在 ActionAccess 中添加一個擴展方法 AddActionAccessControl,將 IResourceProvider 添加進去,這樣就可以在 ResourceProviderHostedService 中讀取到

using DotNetNB.Security.Core;
using DotNetNB.Security.Core.Extensions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace DotNetNB.Security.ActionAccess
{
    public static class SecurityOptionExtensions
    {
        public static SecurityOption AddActionAccessControl(this SecurityOption option)
        {
            option.Services.TryAddEnumerable(ServiceDescriptor.Transient<IResourceProvider, ActionResourceProvider>());
            return option;
        }
    }
}

在 ResourceProviderHostedService 的構造函數中讀取 IServiceProvider

using Microsoft.Extensions.Hosting;

namespace DotNetNB.Security.Core
{
    public class ResourceProviderHostedService : IHostedService
    {
        private readonly IServiceProvider[] _serviceProviders;

        public ResourceProviderHostedService(IServiceProvider[] serviceProviders)
        {
            _serviceProviders = serviceProviders;
        }

        public async Task StartAsync(CancellationToken cancellationToken)
        {

        }

        public async Task StopAsync(CancellationToken cancellationToken)
        {

        }
    }
}

在 EntityAccess 中同樣添加一個擴展方法 AddEntityAccessControl

using DotNetNB.Security.Core;
using DotNetNB.Security.Core.Extensions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace DotNetNB.Security.EntityAccess
{
    public static class SecurityOptionExtensions
    {
        public static SecurityOption AddEntityAccessControl(this SecurityOption option)
        {
            option.Services.TryAddEnumerable(ServiceDescriptor.Transient<IResourceProvider, EntityResourceProvider>());
            return option;
        }
    }
}

EntityResourceProvider 繼承 IResourceProvider

using DotNetNB.Security.Core;
using DotNetNB.Security.Core.Models;

namespace DotNetNB.Security.EntityAccess
{
    public class EntityResourceProvider : IResourceProvider
    {
        public async Task<IEnumerable<Resource>> ExecuteAsync()
        {
            return new List<Resource>();
        }
    }
}

完成之後在 DotNetNB.WebApplication 中添加項目引用,就可以進行配置

builder.Services.AddSecurity(options =>
{
    options.AddActionAccessControl()
        .AddEntityAccessControl();
});

後面再完善 AddEntityAccessControl 加入 DBContext

GitHub源碼鏈接:

//github.com/MingsonZheng/dotnetnb.security

課程鏈接

//appsqsyiqlk5791.h5.xiaoeknow.com/v1/course/video/v_5f39bdb8e4b01187873136cf?type=2

知識共享許可協議

本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。

歡迎轉載、使用、重新發布,但務必保留文章署名 鄭子銘 (包含鏈接: //www.cnblogs.com/MingsonZheng/ ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發布。

如有任何疑問,請與我聯繫 ([email protected]) 。