用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實現基於角色的訪問控制

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM