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

MingsonZheng發表於2022-02-18

目錄

  • 開發任務
  • 程式碼實現

開發任務

  • DotNetNB.Security.Core:定義 core,models,Istore;實現 default memory store
  • DotNetNB.Security.Identity:將許可權賦予角色或使用者;在使用者登入時將 Permissions 寫入使用者身份 claims

程式碼實現

新增一個 Identity 的擴充套件,將 role 和 Permission 結合到一起

定義 IRolePermissionManager 介面,提供一個 AddRolePermission 的方法

using DotNetNB.Security.Core.Models;

namespace DotNetNB.Security.Identity
{
    public interface IRolePermissionManager<TRole>
    {
        public Task AddRolePermission(TRole role, Permission permission);
    }
}

新增 RolePermissionManager 繼承 IRolePermissionManager,將 resource 的 key 存到 role 的 Claim 中

因為這樣只在 role 裡面記錄了 key,不知道來自哪個 permission,所以還需要持久化 permission 和 role 的關係

using DotNetNB.Security.Core;
using DotNetNB.Security.Core.Models;
using Microsoft.AspNetCore.Identity;
using System.Security.Claims;

namespace DotNetNB.Security.Identity
{
    public class RolePermissionManager<TRole> : IRolePermissionManager<TRole> where TRole : class
    {
        private readonly RoleManager<TRole> _roleManager;

        public RolePermissionManager(RoleManager<TRole> roleManager)
        {
            _roleManager = roleManager;
        }

        public async Task AddRolePermission(TRole role, Permission permission)
        {
            foreach (var resource in permission.Resources)
            {
                await _roleManager.AddClaimAsync(role, new Claim(ClaimsTypes.Permission, resource.Key));
            }

            //TBD 持久化 permission 和 role 的關係
        }
    }
}

由於無法直接獲取到 Permission,只能獲取到 Permission 的 key,所以需要在 IPermissionManager 介面中新增一個 GetAsync 方法

public Task<Permission> GetAsync(string key);

在 PermissionManager 中實現 GetAsync 方法

public async Task<Permission> GetAsync(string key)
{
    return await _permissionStore.GetByKeyAsync(key);
}

這樣在 RolePermissionManager 中就可以通過這個方法獲取到 Permission

using DotNetNB.Security.Core;
using Microsoft.AspNetCore.Identity;
using System.Security.Claims;

namespace DotNetNB.Security.Identity
{
    public class RolePermissionManager<TRole> : IRolePermissionManager<TRole> where TRole : class
    {
        private readonly IPermissionManager _permissionManager;
        private readonly RoleManager<TRole> _roleManager;

        public RolePermissionManager(IPermissionManager permissionManager, RoleManager<TRole> roleManager)
        {
            _permissionManager = permissionManager;
            _roleManager = roleManager;
        }

        public async Task AddRolePermission(TRole role, string permissionKey)
        {
            var permission = await _permissionManager.GetAsync(permissionKey);
            if (permission == null)
            {
                throw new InvalidOperationException($"Permission not found:{permissionKey}");
            }
            foreach (var resource in permission.Resources)
            {
                await _roleManager.AddClaimAsync(role, new Claim(ClaimsTypes.Permission, resource.Key));
            }

            //TBD 持久化 permission 和 role 的關係
        }
    }
}

實際上使用者可能也獲取不到 TRole,需要通過 roleId 查詢

public async Task AddRolePermission(string roleId, string permissionKey) 
{
    var role = await _roleManager.FindByIdAsync(roleId);
    if (role == null)
    {
        throw new InvalidOperationException($"Role not found:{roleId}");
    }

    var permission = await _permissionManager.GetAsync(permissionKey);
    if (permission == null)
    {
        throw new InvalidOperationException($"Permission not found:{permissionKey}");
    }
    foreach (var resource in permission.Resources)
    {
        await _roleManager.AddClaimAsync(role, new Claim(ClaimsTypes.Permission, resource.Key));
    }

    //TBD 持久化 permission 和 role 的關係
}

使用者也是一樣的做法,定義一個 IUserPermissionManager 介面,提供一個 AddUserPermission 方法

namespace DotNetNB.Security.Identity
{
    public interface IUserPermissionManager<TUser>
    {
        public Task AddUserPermission(string userId, string permissionKey);
    }
}

新增 UserPermissionManager 繼承 IUserPermissionManager,將 Permission 中所有 Resource 的 key 作為 User 的 Claim

using System.Security.Claims;
using DotNetNB.Security.Core;
using Microsoft.AspNetCore.Identity;

namespace DotNetNB.Security.Identity
{
    public class UserPermissionManager<TUser> : IUserPermissionManager<TUser> where TUser : class
    {
        private readonly UserManager<TUser> _userManager;
        private readonly IPermissionManager _permissionManager;

        public UserPermissionManager(UserManager<TUser> userManager, IPermissionManager permissionManager)
        {
            _userManager = userManager;
            _permissionManager = permissionManager;
        }

        public async Task AddUserPermission(string userId, string permissionKey)
        {
            var user = await _userManager.FindByIdAsync(userId);
            if (user == null)
            {
                throw new InvalidOperationException($"User not found:{userId}");
            }

            var permission = await _permissionManager.GetAsync(permissionKey);
            if (permission == null)
            {
                throw new InvalidOperationException($"Permission not found:{permissionKey}");
            }

            var claims = permission.Resources.Select(p => new Claim(ClaimsTypes.Permission, p.Key)).ToList();
            await _userManager.AddClaimsAsync(user, claims);
        }
    }
}

提供一個擴充套件方法將 RolePermissionManager 和 UserPermissionManager 新增到程式中

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;

namespace DotNetNB.Security.Identity.Extensions
{
    public static class IdentityBuilderExtensions
    {
        public static IdentityBuilder WithPermissions<TUser, TRole>(this IdentityBuilder identityBuilder)
            where TRole : class where TUser : class
        {
            identityBuilder.Services.AddScoped<IRolePermissionManager<TRole>, RolePermissionManager<TRole>>()
                .AddScoped<IUserPermissionManager<TUser>, UserPermissionManager<TUser>>();
            return identityBuilder;
        }
    }
}

GitHub原始碼連結:

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

課程連結

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

知識共享許可協議

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

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

如有任何疑問,請與我聯絡 (MingsonZheng@outlook.com) 。

相關文章