Asp.net core IdentityServer4与传统基于角色的权限系统的集成

目录

写在前面

开始之前

实现思路

开始实现

服务端

1、生成自定义token

客户端

1、自定义授权标签CustomRBACAuthorize

2、自定义授权 IAuthorizationRequirement

3、自定义IAuthorizationPolicyProvider

4、自定义Requirement的的 AuthorizationHandler

5、注册自定义授权处理程序

6、在接口上使用自定义授权标签CustomRBACAuthorize

7、测试结果

管理员1001 角色id R01  Alice

普通用户1002 角色id R02  Bob

总结

源码

参考


写在前面

因为最近在忙别的,好久没水文了 今天来水一篇;

在学习或者做权限系统技术选型的过程中,经常有朋友有这样的疑问 :

“IdentityServer4的能不能做到与传统基于角色的权限系统集成呢?”

“我的公司有几百个接口,IdentityServer4能不能做到关联用户,给这些用户授予不同的接口的权限呢?”

我的回答是,是的,可以!

同时,我还想补充下,IdentityServer4是给我们的授权流程/需求提供一个新的 标准化的选择,而不是限制你的需求;它是一个基础的框架,你可以根据你的需求自定义成任意你要的样子。

OK,下面开始说说我的实现思路,不一定最优只为抛砖引玉。

开始之前

先准备好两个WebApi 项目,分别有两个接口

Hei.UserApi:6001

GetUsername: https://localhost:6001/api/profile/getusername

GetScore:https://localhost:6001/api/Credit/GetScore //用户信用分要求高,期望管理员才可以调用

Hei.OrderApi:6002

GetOrderNo:https://localhost:6002/api/Order/GetOrderNo

GetAddress:https://localhost:6002/api/Delivery/GetAddress //用户地址敏感,期望管理员才可以调用

实现请看源码

准备好两个角色:

R01 管理员

R02 普通用户

准备好两个用户

Bob: subid=1001,普通用户

Alice: subid=1002,管理员

实际用户有多个角色的,本文为了简化问题,一个用户只允许一种角色

角色对应的权限

管理员:可以调用 Hei.UserApiHei.OrderApi的所有接口;

普通用户:只可以调用 Hei.UserApi->GetUsername,和Hei.OrderApi->GetOrderNo;

实现思路

先来看晓晨大佬画的 access_token 验证交互过程图:

img

image-20220223112832900

可以看到,Token在首次被服务端验证后,后续的验证都在客户端验证的,本文的重点就在这里,需要判断token有没有权限,重写这部分即可;

开始实现

服务端

1、生成自定义token

1、 IdentityServer4 服务端重写IResourceOwnerPasswordValidator 和 IProfileService 两个接口生成携带有自定义信息的access_token

public class CustomResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{
    public CustomResourceOwnerPasswordValidator()
    {
    }

    public Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
    {
        if (!string.IsNullOrEmpty(context.UserName) && !string.IsNullOrEmpty(context.Password))
        {
            var loginUser = UserService.Users.First(c => c.Username == context.UserName && c.Password == context.Password);

            if (loginUser != null)
            {
                context.Result = new GrantValidationResult(loginUser.SubjectId, OidcConstants.AuthenticationMethods.Password, new Claim[]{new Claim("my_phone","10086")}); //这里增加自定义信息
                return Task.CompletedTask;
            }
        }
        return Task.CompletedTask;
    }
}

StartUp.cs 启用

builder.AddResourceOwnerValidator<CustomResourceOwnerPasswordValidator>();
builder.AddProfileService<CustomProfileService>();

2、请求一个token来看看:

image-20220223115450490

image-20220223115310375

可以看到我这里token携带有了自定义信息 my_phone,同样的,你可以把角色id直接放这里,或者直接跟用户的subid关联(本demo就是);

客户端

1、自定义授权标签CustomRBACAuthorize

    public class CustomRBACAuthorizeAttribute : AuthorizeAttribute
    {
        public CustomRBACAuthorizeAttribute(string policyName="")
        {
            this.PolicyName = policyName;
        }

        public string PolicyName
        {
            get
            {
                return PolicyName;
            }
            set
            {
                Policy = $"{Const.PolicyCombineIdentityServer4ExternalRBAC}{value.ToString()}";
            }
        }
    }

后面接口打这个标签就表示使用基于自定义的与权限校验

2、自定义授权 IAuthorizationRequirement

   public class CustomRBACRequirement: IAuthorizationRequirement
    {
        public string PolicyName { get; }

        public CustomRBACRequirement(string policyName)
        {
            this.PolicyName = policyName;
        }
    }

3、自定义IAuthorizationPolicyProvider

public class CustomRBACPolicyProvider : IAuthorizationPolicyProvider
    {
        private readonly IConfiguration _configuration;
        public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; }

        public CustomRBACPolicyProvider(IConfiguration configuration, IOptions<AuthorizationOptions> options)
        {
            _configuration = configuration;
            FallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
        }


        public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
        {
            return FallbackPolicyProvider.GetDefaultPolicyAsync();
        }

        public Task<AuthorizationPolicy> GetFallbackPolicyAsync()
        {
            return Task.FromResult<AuthorizationPolicy>(null);
        }

        public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
        {
            if (policyName.StartsWith(Const.PolicyCombineIdentityServer4ExternalRBAC, StringComparison.OrdinalIgnoreCase))
            {
                var policys = new AuthorizationPolicyBuilder();
                //这里使用自定义Requirement
                policys.AddRequirements(new CustomRBACRequirement(policyName.Replace(Const.PolicyCombineIdentityServer4ExternalRBAC,"")));
                return Task.FromResult(policys.Build());
            }

            return Task.FromResult<AuthorizationPolicy>(null);

        }
      }  

4、自定义Requirement的的 AuthorizationHandler

/// <summary>
/// 处理CustomRBACRequirement的逻辑
/// </summary>
/// <param name="context"></param>
/// <param name="requirement"></param>
/// <returns></returns>
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomRBACRequirement requirement)
{
    var subid = context.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    var routeData = _httpContextAccessor.HttpContext?.GetRouteData();

    var curentAction = routeData?.Values["action"]?.ToString();
    var curentController = routeData?.Values["controller"]?.ToString();

    //入口程序集,用来标识某个api
    var apiName = Assembly.GetEntryAssembly().GetName().Name;

    if (string.IsNullOrWhiteSpace(subid) == false && string.IsNullOrWhiteSpace(curentAction) == false && string.IsNullOrWhiteSpace(curentController) == false)
    {
        //核心就在这里了,查出用户subid对应的角色权限,然后做处理判断有没有当前接口的权限
        //我这里是demo就简单的模拟下,真实的权限数据应该都是写数据库或接口的
        var userPermission = PermissionService.GetUserPermissionBySubid(apiName, subid);
        if (userPermission != null && userPermission.Authorised.ContainsKey(curentController))
        {                    
            var authActions = userPermission.Authorised[curentController];
                    
            //这里判断当前用户的角色有当前action/controllers的权限
            //(真实的权限划分由你自己定义,比如你划分了只读接口,只写接口、特殊权限接口、内部接口等,在管理后台上分组,打标签/标记然后授予角色就行)
            if (authActions?.Any(action => action == curentAction) == true)
            {
                context.Succeed(requirement);
            }
        }
    }

    return Task.CompletedTask;
}

jwt 的token本来是去中心化的,现在这样一来,每次请求进来都去调接口验证可以说是违背了去中心化的思想,所以保证性能问题得自己解决;

权限数据

public class PermissionService
{
    /// <summary>
    /// 权限信息(实际上这些应该存在数据库)
    /// </summary>
    public static List<PermissionEntity> Permissions = new List<PermissionEntity>
    {
        //RoleId R01 是管理员,有两个Api的多个接口的权限
        new PermissionEntity{ PermissionId="0001",RoleId="R01", ApiName="Hei.UserApi",Authorised=new Dictionary<string, List<string>>
            {
                { "Profile",new List<string>{ "GetUsername"}},
                { "Credit",new List<string>{ "GetScore"}},
            }
        },
        new PermissionEntity{ PermissionId="0002",RoleId="R01", ApiName="Hei.OrderApi",Authorised=new Dictionary<string, List<string>>
            {
                { "Delivery",new List<string>{ "GetAddress"}},
                { "Order",new List<string>{ "GetOrderNo"}},
            }
        },

        //RoleId R02 是普通员工,有两个Api的多个 部分 接口的权限
        new PermissionEntity{ PermissionId="0001",RoleId="R02", ApiName="Hei.UserApi",Authorised=new Dictionary<string, List<string>>
            {
                { "Profile",new List<string>{ "GetUsername"}},
                //{ "Credit",new List<string>{ "GetScore"}}, //用户信用分接口权限就不给普通员工了
            }
        },
        new PermissionEntity{ PermissionId="0002",RoleId="R02", ApiName="Hei.OrderApi",Authorised=new Dictionary<string, List<string>>
            {
                //{ "Delivery",new List<string>{ "GetAddress"}}, //用户地址信息也是
                { "Order",new List<string>{ "GetOrderNo"}},
            }
        }
    };

当然这些数据一般都是根据你的权限需求存数据库的,与你的权限管理后台相配合;

5、注册自定义授权处理程序

     /// <summary>
        /// 提交自定义角色的授权策略
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        public static IServiceCollection AddCustomRBACAuthorizationPolicy(this IServiceCollection services)
        {
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.AddSingleton<IAuthorizationPolicyProvider, CustomRBACPolicyProvider>();
            services.AddSingleton<IAuthorizationHandler, CustomRBACRequirementHandler>();

            return services;
        }

6、在接口上使用自定义授权标签CustomRBACAuthorize

    [Route("api/[controller]/[action]")]
    [ApiController]
    public class CreditController : ControllerBase
    {
        /// <summary>
        /// 获取信用分
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet]
        [CustomRBACAuthorize] //这里就表名
        public int GetScore(string id)
        {
            return 666;
        }
    }

7、测试结果

管理员1001 角色id R01  Alice

image-20220223151041196

请求:

image-20220223152252049

可以看到都是 200

普通用户1002 角色id R02  Bob

image-20220223151144846

请求:

image-20220223152233656

可以看到获取用户信用积分、订单投递地址的接口403了,与我们全面的设定相符;

总结

就是一个简单的思路

1、给access_token 带上自定义信息;

2、在客户端重写本地验证/权限校验逻辑即可;

其实token黑白名单,token撤销原理类似 希望能帮上一点小忙;

IdentityServer4就是一个工具,希望大家不要给它设定太多的限制“不能做这个,不能做那个等等”

源码

https://github.com/gebiWangshushu/cnblogs-demos/tree/dev/IdentityServerWithRBAC.Example

如果能有个小星星那就再好不过了(✧◡✧)

参考

https://docs.microsoft.com/zh-cn/aspnet/core/security/authorization/iauthorizationpolicyprovider?view=aspnetcore-3.1#multiple-authorization-policy-providers

https://www.cnblogs.com/stulzq/p/9226059.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我会尽力回答你的问题。基于 ASP.NET Core MVC 的固定资产管理系统可以分为以下几个部分的设计和实现: 1. 数据库设计:需要设计数据库表,包括资产信息表、领用信息表、报废信息表等。其中资产信息表需要包含资产名称、规格型号、使用部门、使用人员等字段;领用信息表需要包含领用人员、领用时间、资产编号等字段;报废信息表需要包含报废原因、报废时间、资产编号等字段。 2. 后台业务逻辑:需要实现资产的增删改查、领用、归还、报废等业务逻辑。同时需要实现权限管理,确保只有授权人员才能进行相关操作。 3. 前端页面设计:需要设计资产列表页面、领用页面、报废页面等。其中资产列表页面需要展示资产的基本信息,包括名称、规格、使用部门等;领用页面需要实现资产的领用操作,包括选择领用的资产、填写领用人员等信息;报废页面需要实现资产的报废操作,包括选择报废的资产、填写报废原因等信息。 4. 数据可视化:需要实现数据可视化功能,包括资产使用情况图表、资产领用情况图表等。这些图表可以帮助管理员更好地了解资产的使用情况,及时进行调整和管理。 以上就是基于 ASP.NET Core MVC 的固定资产管理系统的设计与实现的主要内容。如果您有更具体的问题或需求,欢迎随时与我交流。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值