• .NET 云原生架构师训练营(权限系统 代码实现 Identity)学习笔记


    目录

    • 开发任务
    • 代码实现

    开发任务

    • 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) 。

  • 相关阅读:
    计算机体系结构的铁律(iron law)
    PHP 画图——使用jpgraph画图
    理解Paxos Made Practical
    【bzoj1015】【JSOI2008】【星球大战】【并查集+离线】
    Spark调研笔记第3篇
    hduoj2094产生冠军
    使用HD/IDE层的ioctl接口获取磁盘容量get_hdd_max_sector
    给GridView设置行高
    tomcat的一些简单配置
    【JavaScript】--JavaScript总结一览无余
  • 原文地址:https://www.cnblogs.com/MingsonZheng/p/15911606.html
Copyright © 2020-2023  润新知