天天看點

.NET 雲原生架構師訓練營(權限系統 代碼實作 Store)

作者:MingsonZheng

目錄

  • 開發任務
  • 代碼實作

  • DotNetNB.Security.Core:定義 core,models,Istore;實作 default memory store
  • DotNetNB.Security.Store.EntityFramework:基于 mysql 建立 PermissionStore 和 ResourceStore
.NET 雲原生架構師訓練營(權限系統 代碼實作 Store)

我們需要在 ResourceProviderHostedService 中讀取所有的 Resource,将 Resource 轉換為 Permission,再将 Permission 配置設定給 Role

建立 Permission 的 model

namespace DotNetNB.Security.Core.Models
{
    public class Permission
    {
        public string Key { get; set; }

        public string DisplayName { get; set; }

        public string Description { get; set; }

        public IEnumerable<Resource> Resources { get; set; }
    }
}
           

在 IPermissionManager 接口中定義 CreateAsync 方法

namespace DotNetNB.Security.Core
{
    public interface IPermissionManager
    {
        public Task CreateAsync(string key, string displayName, string description, IEnumerable<string> resources);
    }
}
           

IResourceManager 接口添加通過 key 擷取 resource 的方法 GetByKeysAsync

public Task<IEnumerable<Resource>> GetByKeysAsync(IEnumerable<string> resources);
           

PermissionManager 裡面需要用到 Store,先定義接口 IPermissionStore

using DotNetNB.Security.Core.Models;

namespace DotNetNB.Security.Core.Store
{
    public interface IPermissionStore
    {
        public Task CreateAsync(Permission permission);
    }
}
           

在 PermissionManager 裡面建立一個 permission,通過 ResourceManager 擷取到所有 resources 指派給 permission,再将 permission 存儲到 PermissionStore

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

namespace DotNetNB.Security.Core;

public class PermissionManager: IPermissionManager
{
    private readonly IResourceManager _resourceManager;
    private readonly IPermissionStore _permissionStore;

    public PermissionManager(IResourceManager resourceManager, IPermissionStore permissionStore)
    {
        _resourceManager = resourceManager;
        _permissionStore = permissionStore;
    }

    public async Task CreateAsync(string key, string displayName, string description, IEnumerable<string> resourceKeys)
    {
        if (string.IsNullOrEmpty(key))
            throw new ArgumentNullException(nameof(key));

        var permission = new Permission { Key = key, DisplayName = displayName, Description = description };
        var resources = await _resourceManager.GetByKeysAsync(resourceKeys);
        permission.Resources = resources;

        await _permissionStore.CreateAsync(permission);
    }
}
           

建立 DefaultPermissionStore 實作 IPermissionStore

using DotNetNB.Security.Core.Models;

namespace DotNetNB.Security.Core.Store
{
    public class DefaultPermissionStore : IPermissionStore
    {
        private List<Permission> _list;

        public DefaultPermissionStore()
        {
            _list = new List<Permission>();
        }

        public async Task CreateAsync(Permission permission)
        {
            _list.Add(permission);
        }
    }
}
           

IPermissionStore 接口添加擷取 permission 清單的方法

public Task<IEnumerable<Permission>> GetAllAsync();
           

在 DefaultPermissionStore 中直接将清單傳回即可

public async Task<IEnumerable<Permission>> GetAllAsync()
{
    return _list;
}
           

在 PermissionManager 中通過 PermissionStore 擷取 Permission 清單傳回

public async Task<IEnumerable<Permission>> GetAllAsync()
{
    return await _permissionStore.GetAllAsync();
}
           

在 IPermissionStore 中添加 GetByKeyAsync 方法,在 PermissionManager 中用于校驗 key 是否存在對應的 Permission

public Task<Permission> GetByKeyAsync(string key);
           

在 DefaultPermissionStore 中實作 GetByKeyAsync 方法

public async Task<Permission> GetByKeyAsync(string key)
{
    return _list.SingleOrDefault(r => r.Key == key);
}
           

在 PermissionManager 中校驗 key 是否存在對應的 Permission

var origin = await _permissionStore.GetByKeyAsync(key);
if (origin != null)
    throw new InvalidOperationException("Duplicated permission key found");
           

IResourceManager 接口添加擷取所有 resource 方法 GetAllAsync

public Task<IEnumerable<Resource>> GetAllAsync();
           

ResourceManager 裡面需要用到 Store,先定義接口 IResourceStore

using DotNetNB.Security.Core.Models;

namespace DotNetNB.Security.Core.Store
{
    public interface IResourceStore
    {
        public Task CreateAsync(Resource resource);

        public Task CreateAsync(IEnumerable<Resource> resources);

        public Task<IEnumerable<Resource>> GetAllAsync();
        
        public Task<Resource> GetByKeyAsync(string key);

        public Task<IEnumerable<Resource>> GetByKeysAsync(IEnumerable<string> resources);
    }
}
           

建立 DefaultResourceStore 實作 IResourceStore

using DotNetNB.Security.Core.Models;

namespace DotNetNB.Security.Core.Store
{
    public class DefaultResourceStore : IResourceStore
    {
        private readonly List<Resource> _list;

        public DefaultResourceStore()
        {
            _list = new List<Resource>();
        }

        public async Task CreateAsync(Resource resource)
        {
            _list.Add(resource);
        }

        public async Task CreateAsync(IEnumerable<Resource> resources)
        {
            _list.AddRange(resources);
        }

        public async Task<IEnumerable<Resource>> GetAllAsync()
        {
            return _list;
        }

        public async Task<Resource> GetByKeyAsync(string key)
        {
            return _list.SingleOrDefault(r => r.Key == key);
        }

        public async Task<IEnumerable<Resource>> GetByKeysAsync(IEnumerable<string> resources)
        {
            return _list.Where(r => resources.Contains(r.Key));
        }
    }
}
           

在 ResourceManager 中通過 ResourceStore 建立存儲擷取 Resource,建立的時候判斷是否已經存在 Resource

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

namespace DotNetNB.Security.Core
{
    public class ResourceManager :  IResourceManager
    {
        private readonly IResourceStore _resourceStore;
        public ResourceManager(IResourceStore resourceStore)
        {
            _resourceStore = resourceStore;
        }

        public async Task CreateAsync(Resource resource)
        {
            var origin = await _resourceStore.GetByKeyAsync(resource.Key);
            if (origin != null)
                throw new InvalidOperationException("Duplicated resource key found");

            await _resourceStore.CreateAsync(resource);
        }

        public async Task CreateAsync(IEnumerable<Resource> resources)
        {
            var origins = await _resourceStore.GetByKeysAsync(resources.Select(r => r.Key));
            if (origins.Any())
                throw new InvalidOperationException(#34;Duplicated resource key found:{string.Concat(origins.Select(o => o.Key), ",")}");

            await _resourceStore.CreateAsync(resources);
        }

        public async Task<IEnumerable<Resource>> GetAllAsync()
        {
            return await _resourceStore.GetAllAsync();
        }

        public async Task<IEnumerable<Resource>> GetByKeysAsync(IEnumerable<string> resources)
        {
            return await _resourceStore.GetByKeysAsync(resources);
        }
    }
}
           

GitHub源碼連結:

https://github.com/MingsonZheng/dotnetnb.security