zoukankan      html  css  js  c++  java
  • 用C#实现基于(OpenId Connect)的单点登录与RBAC权限验证(SSO,OIDC,RBAC)

    一、项目需求

           由于微服务技术的发展,需要对老的项目进行升级改造。其中一大难点就是老项目中使用了RBAC的权限系统,

    面向微服务,首先要完成界面和认证后台的分离。

          于是,对面向微服务的 RBAC 系统提出了如下需求:

           A、认证系统需要遵守当前流行的 Auth2.0 协议,从而支持到单点登录SSO;

      B、客户端使用纯 js 开发,做到前后台分离;

      C、实现 RBAC 权限控制。

      针对需求,查了当前C#开源,对 RBAC 与 OpenId Connect 的结合比较有限。对是自己动手丰衣足食,提出了如下解决方案:

    二、解决方案

    1、总体解决方案及参考项目

      针对需求的每一项提出解决方案

           A、后台采用 OpenId Connect 包作 Auth2.0 协议的支持。github上源码:  openiddict-core

      B、客户端使用 D2Admin + oidc-client.js  和后台配合完成 SSO 和 Auth2.0 的交互认证;

      C、由于 Auth2.0 并不支持 RBAC 权限认证,自己开发了一个  Rbac.Auth.Introspection 工程,用来完成资源程序中的RBAC认证,

    并且完全不破坏 Auth2.0 协议。本质上是对 Auth2.0 的扩展。github上的原始参考项目:AspNet.Security.OAuth.Extensions

           开发好的库可以直接用 Nuget 获得:https://www.nuget.org/packages/Rbac.Auth.Introspection/

    2、Auth2.0 与 RBAC 整合的基本原理

          默认的认证中只要 Access Token 合法即通过,改为 Access Token 和 Url 同时合法才能通过即可。

    Url 作为基础的权限的参数,权限分配到角色,人员和再分配不同角色。

         后台认证时,一个 Access Token 和 Url 进来,通过 Access Token 可查到人员,从而查到该人员是否有对应权限,如果有对应的 Url 

    则返回认证成功,否则失败。

         可以看出:是在标准的 Auth2.0 的 Acess Token 认证交互中扩展了 Url 参数参与认证,从而实现了 RBAC 的认证。

                // Note: always specify the token_type_hint to help
                // the authorization server make a faster token lookup.
                var parameters = new Dictionary<string, string>
                {
                    [RbacAuthIntrospectionConstants.Parameters.Token] = token,
                    [RbacAuthIntrospectionConstants.Parameters.Path] = path,
                    [RbacAuthIntrospectionConstants.Parameters.TokenTypeHint] = RbacAuthIntrospectionConstants.TokenTypeHints.AccessToken
                };
    
                ...
    
                request.Content = new FormUrlEncodedContent(parameters);
    
                var notification = new SendIntrospectionRequestContext(Context, Scheme, Options, request, path, token);
                await Events.SendIntrospectionRequest(notification);

    3、对于资源端权限定义的扩展

         每个微服务都有自己提供服务的 Url 接口,这对于 RBAC 系统的配置提出了巨大的工作量,还需小心谨慎。有没有自动方化的方法,

    减少配置的工作量和出错的可能性。为此在 Rbac.Auth.Introspection 中扩展了权限定义和权限列表生成接口。

      3.1 资源端的初始化

           // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddAuthentication(options =>
                {
                    options.DefaultScheme = RbacAuthIntrospectionDefaults.AuthenticationScheme;
                })
                .AddRbacAuthIntrospection(options =>
                {
                    options.Authority = new Uri("http://localhost:12345/");
                    options.Audiences.Add("resource-server-2");
                    options.ClientId = "resource-server-2";
                    options.ClientSecret = "C744604A-CD05-4092-9CF8-ECB7DC3499A2";
                    options.RequireHttpsMetadata = false;
                });
    
                services.AddMvc(options => { options.EnableEndpointRouting = false; });
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                app.UseCors(builder =>
                {
                    builder.WithOrigins(new string[] { "http://localhost:12345", "http://localhost:8080" });
                    builder.WithMethods("GET");
                    builder.WithHeaders("Authorization");
                });
    
                app.UseStaticFiles();
                app.UseAuthentication();
                app.UseMvc(routes =>
                {
                    routes.MapRoute(
                            name: "default",
                            template: "{controller=Home}/{action=Index}/{id?}");
                });
    
            }

        通过指定 RbacAuthIntrospectionDefaults.AuthenticationScheme 来绑定 Rbac 的权限认证;

             通过 AddRbacAuthIntrospection 设置服务器相关的参数;

        通过 app.UseAuthentication 激活权限认证。

         3.2 扩展了权限定义。

      目的:方便自动化生成权限列表,并且和 Asp.Net Core 的 Identity 权限系统完美整合。

        [AuthorizeMenu("资源示例", AuthenticationSchemes = RbacAuthIntrospectionDefaults.AuthenticationScheme)]
        public class ResourceController : Controller
        {
            [HttpGet]
            [PermissionFilter(PermissionType.Menu, "私有功能", Page = "/home/test")]
            public IActionResult Private()
            {
                var identity = User.Identity as ClaimsIdentity;
                if (identity == null)
                {
                    return BadRequest();
                }
    
                return Content($"You have authorized access to resources belonging to {identity.Name} on ResourceServer01.");
            }
    
            [HttpGet]
            [AllowAnonymous]
            public IActionResult Public()
            {
                return Content("This is a public endpoint that is at ResourceServer01; it does not require authorization.");
            }
        }

       其中的   AuthorizeMenu 和  PermissionFilter 便是自定义的扩展,可以直接生成对应的权限。减少工作量和出错机会。

    每次修正代码后也方便自动修正后台服务器中对应的权限,保持系统与代码的一至性。

            3.3  如何自动化生成权限列表

      对于资源端,通过  ApplicationController 的 GetPermissionList 接口来获得权限列表。(appId 是用于标识本资源的唯一Id)

        public class ApplicationController : Controller
        {
            [HttpGet]
            public IActionResult GetPermissionList(string appId)
            {
                var resList = new List<PermissionDTO>();
                var controllerList = GetTypes().Where(t => t.IsSubclassOf(typeof(Controller))).ToList();
                if (controllerList.Count > 0)
                {
                    resList = ControllerTools.CreatePermissionDTOList(appId, controllerList);
                }
    
                var res = new JsonResult(new
                {
                    code = 0,
                    msg = "",
                    data = JsonConvert.SerializeObject(resList)
                });
                return res;
            }
    
            private IEnumerable<Type> GetTypes()
            {
                foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
                    foreach (var t in GetTypes(asm))
                        yield return t;
            }
    
            private IEnumerable<Type> GetTypes(System.Reflection.Assembly asm)
            {
                Type[] ts;
                try
                {
                    ts = asm.GetTypes();
                }
                catch { yield break; }
    
                foreach (var t in ts)
                    yield return t;
            }
        }

    三、参考

      基于OIDC(OpenID Connect)的SSO

     OIDC(OpenId Connect)身份认证(核心部分)

     HANDLING ACCESS TOKENS FOR PRIVATE APIS IN ASP.NET CORE

     RBAC | 使用authing实现基于角色的访问控制

      

  • 相关阅读:
    云端开发,云端部署
    Chrome下的Page Speed使用
    Linux的到来
    NoSQL 之 Morphia 操作 MongoDB
    qTip2
    在.NET下使用Task Parallel Library提高程序性能
    WCF REST系列文章汇总
    Google工具pagespeed使用教程
    从零开始系统深入学习android
    如何优化一个网站使之提高访问速度
  • 原文地址:https://www.cnblogs.com/citycomputing/p/13221295.html
Copyright © 2011-2022 走看看