Blazor 下支持 Azure AD 的多套登录方案

news2024/11/20 6:33:50

比如上图配置了两套不同的登录方案,各有自己的 TenantId 和 ClientId ,要同时支持他们的登录(其实在同一套 TenantId 和 ClientId 里面配置多个登录账户不就好了,但是......那套登录的管理是在客户自己的Azure AD账户管理下的,而作为技术支持不想麻烦客户,更何况客户不一定同意呢,所以需要第二套专为技术支持提供的用户组......那么就自己再弄一套吧)

然后问题就来了,在Blazor 页面要触发验证需要调用 HttpContext.ChallengeAsync,你可以试试在.razor 组件内调用 HttpContextAccessor.HttpContext.ChallengeAsync 会发生什么......
当你执行的时候,由于Blazor 使用的是 WebSocket 所以这个 Http 的处理方式就报错了,说你的请求头有问题,是不是很无语?

那么怎么解决这个问题呢?在Asp.net Core 3.0 就加入了 EndPoints 终结点的概念,看一下 ChatGPT 是怎么说的

由此看来 EndPoints 可以自定义的控制路由访问,比Controller更加强大

所以这个时候搞明白一件事情,对于多个 oidc 的登录,需要自己用一特定路由地址来实现

这个工作都在 StartUp 里完成,只列出核心代码

1. 注册两套 OIDC 登录方案,注意他们的 CallbackPath  不能是一样的

        public void ConfigureServices(IServiceCollection services)
        {
            ......

            services.AddRazorPages();
            services.AddServerSideBlazor();

            // Configure authentication
            var authorityFormat = Configuration["AzureAd:Authority"];
            var callbackPath = Configuration["AzureAd:CallbackPath"];

            services.AddAuthentication(options =>
            {
                options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            })
            .AddCookie()
            .AddOpenIdConnect(_customerAuthType, options =>
            {
                options.Authority = string.Format(CultureInfo.InvariantCulture, authorityFormat, Configuration[$"AzureAd:{_customerAuthType}:TenantId"]);
                options.ClientId = Configuration[$"AzureAd:{_customerAuthType}:ClientId"];
                options.CallbackPath = Configuration[$"AzureAd:{_customerAuthType}:CallbackPath"];
                options.ResponseType = OpenIdConnectResponseType.IdToken; // Use implicit flow
                options.Scope.Add("openid");
                options.Scope.Add("profile");
                options.Events = new OpenIdConnectEvents
                {
                    OnTokenValidated = context =>
                    {
                        var identity = context.Principal.Identity as ClaimsIdentity;
                        identity.AddClaim(new Claim(_authScheme, _customerAuthType));
                        return Task.CompletedTask;
                    }
                };
            })
            .AddOpenIdConnect(_supportAgentAuthType, options =>
            {
                options.Authority = string.Format(CultureInfo.InvariantCulture, authorityFormat, Configuration[$"AzureAd:{_supportAgentAuthType}:TenantId"]);
                options.ClientId = Configuration[$"AzureAd:{_supportAgentAuthType}:ClientId"];                                
                options.CallbackPath = Configuration[$"AzureAd:{_supportAgentAuthType}:CallbackPath"];                
                options.ResponseType = OpenIdConnectResponseType.IdToken;
                options.Scope.Add("openid");
                options.Scope.Add("profile");
                options.Events = new OpenIdConnectEvents
                {
                    OnTokenValidated = context =>
                    {
                        var identity = context.Principal.Identity as ClaimsIdentity;
                        identity.AddClaim(new Claim(_authScheme, _supportAgentAuthType));
                        return Task.CompletedTask;
                    }
                };
            });

            services.AddAuthorization();
            ......
        }

2. 使用 Endpoints 响应自己定义的路由处理 (登录和登出)

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            ......

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                endpoints.MapBlazorHub();
                endpoints.MapFallbackToPage("/_Host");

                // Add endpoints for login challenges
                endpoints.MapGet("/login-customer", async context =>
                {
                    await context.ChallengeAsync(_customerAuthType, new AuthenticationProperties
                    {
                        RedirectUri = "/"
                    });
                });

                endpoints.MapGet("/login-support-agent", async context =>
                {
                    await context.ChallengeAsync(_supportAgentAuthType, new AuthenticationProperties
                    {
                        RedirectUri = "/"
                    });
                });

                // Add endpoint for logout
                endpoints.MapGet("/logout", async context =>
                {
                    var user = context.User;
                    if (user.Identity.IsAuthenticated)
                    {
                        var authSchemeClaim = user.FindFirst(_authScheme);
                        if (authSchemeClaim != null)
                        {
                            var authScheme = authSchemeClaim.Value;
                            var tenant = user.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid")?.Value;
                            await context.SignOutAsync(authScheme);

                            // sign out from IDP
                            if (tenant != null)
                            {
                                // Construct the current full URL                                
                                var currentUrl = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.PathBase}";
                                context.Response.Redirect($"https://login.microsoftonline.com/{tenant}/oauth2/v2.0/logout?post_logout_redirect_uri={currentUrl}");
                            }
                        }
                    }
                    await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);                    
                });
            });
        }

可以看到上面分别注册了三个路由 /login-customer,/login-support-agent,/logout

一个给客户登录用,一个给技术支持登录用,最后一个是登出,

这个时候再利用 HttpContext 去 Challenge 就不会报错了,那么 blazor 页面上所做就是跳转到上面的路由地址就可以实现相应的登录和登出了

    private void SupportAgentLogin()
    {
        navigation.NavigateTo("/login-support-agent", true);
    }
    private void Logout()
    {
        navigation.NavigateTo("/logout", true);
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1687013.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++BuilderXE 如何让listView按文件名数字排序而非字母排序

int m_nDataColSort0; bool IsAsctrue; void __fastcall TForm1::RzListView4Compare(TObject *Sender, TListItem *Item1, TListItem *Item2, int Data, int &Compare) { if(m_nDataColSort0) { //按列表第二列排序 //CompareCompareText(Item1->SubItems-…

新书发布——《机器学习大数据平台的构建、任务实现与数据治理——使用Azure、DevOps、MLOps》

内容简介 机器学习大数据平台的构建、任务实现与数据治理 你需要构建安全、稳定的数据平台,需要可以扩展到任何规模的工作负载。当项目从实验室进入生产环境时,你需要确信它可以应对现实工作中的挑战。本书能够帮助你实现这些需求,将讲述如…

ARM9驱动开发基础概念

2、arm9的通用寄存器有几个? 3、异常向量表中irq的异常向量是多少? 4、cpsr中的那几位是用来设置工作模式的? 5、r13,r14,15别名是什么?有什么作用? r13栈顶指针 :它用于实现堆栈指针操作,实…

安装测缝计安装事项详解

在建筑和工程领域,测量缝隙和裂缝的准确性对于工程质量和安全性至关重要。测缝计作为一种专业的测量工具,能够帮助工程师和施工人员准确测量和监测建筑结构的缝隙情况,进而采取合适的修复和加固措施,保证建筑物的稳定性和安全性。…

YOLOv8独家改进:KAN系列 | 「一夜干掉MLP」的KAN ,全新神经网络架构一夜爆火

💡💡💡创新点:来自 MIT 等机构的研究者提出了一种非常有潜力的替代方法 KAN。该方法在准确性和可解释性方面表现优于 MLP。而且,它能以非常少的参数量胜过以更大参数量运行的 MLP。 KAN 在边上具有激活函数,而 MLP 在节点上具有激活函数。KAN 似乎比 MLP 的参数效率更…

预约直播丨ETLCloud训练营:ETL中多流数据合并与运算专题

在大数据处理领域,一个至关重要的步骤是对多源数据流进行汇聚与融合,进而开展深度处理与剖析。此操作对于构建高效数据仓库、实现数据动态变化的实时洞察,以及驾驭复杂事件流的处理机制尤为关键。过程涉及从多样化的数据源中抽取信息&#xf…

【408精华知识】Cache类题目解题套路大揭秘

有关Cache的题目,需要理解Cache的工作原理,也即给出一个地址,要知道如何在Cache中寻找或者如何将其从主存中复制入Cache,同时理解Cache中具体是如何存储的,包含三种存储方式,分别是直接映射、全相联映射、组…

最有效的企业数据防泄漏手段 | 数据泄漏防护系统推荐

随意信息安全意识不断提高,企业纷纷寻求高效的数据防泄漏手段。在众多解决方案中,这五款软件各具特色,但它们的共同目标都是确保企业数据的安全性和保密性。 接下来,我们将逐一介绍这五款软件的特点和优势。 1、Ping 32 Ping32…

前端面试项目细节重难点(已工作|做分享)

面试官提问:需求场景:页面上有一个单选框,有是否两个选项:当用户选择是,出现一个输入框,用户可以输入内容,给后端的保存接口传入参数radio和content这两个字段,值分别是用户选项和输…

【Python】 如何在Python中创建GUID UUID

基本原理 GUID(全局唯一标识符)和UUID(通用唯一标识符)都是用来在分布式系统中唯一标识信息的。在Python中,我们可以使用内置的uuid模块来生成这些唯一标识符。 UUID有几种不同的版本,每种版本都有其特定…

图形学初识--光栅化直线算法

文章目录 什么叫做光栅化?为什么需要光栅化?直线的光栅化算法有哪些?Bresemham算法问题定义:问题模型简化:算法核心理解:算法拓展: 结尾:喜欢的小伙伴可以点点关注赞哦 什么叫做光栅…

【Java】手把手学会数组的使用

数组的基本用法 创建数组 基本语法: // 动态初始化 数据类型 [] 数组名称 new 数据类型 [] { 初始化数据 }; // 静态初始化 数据类型 [] 数组名称 { 初始化数据 }; 代码示例: int[] array1 {1,2,3,4,5};int[] array2 new int[]…

如何在OpenHarmony上使用SeetaFace2人脸识别库?

简介 相信大部分同学们都已了解或接触过OpenAtom OpenHarmony(以下简称“OpenHarmony”)了,但你一定没在OpenHarmony上实现过人脸识别功能,跟着本文带你快速在OpenHarmony标准设备上基于SeetaFace2和OpenCV实现人脸识别。 项目效…

c++ 实现 梯度下降线性回归模型

理论与python实现部分 3.1. 线性回归 — 动手学深度学习 2.0.0 documentation c代码 没能力实现反向传播求梯度&#xff0c;只能自己手动算导数了 #include <bits/stdc.h> #include <time.h> using namespace std;//y_hat X * W b // linreg 函数&#xff1a…

9 个适用于小型企业的顶级API管理解决方案

应用程序接口管理解决方案可帮助各种规模的企业开发、部署和管理其应用程序接口&#xff0c;并实现收入最大化。 建立 API 的组织和开发人员可能会被整个 API 生命周期中需要完成的大量任务压得喘不过气来。从规划和构建到部署、维护和货币化&#xff1b;这是一项具有挑战性的工…

【计算机网络原理】对传输层TCP协议的重点知识的总结

˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如…

7.从0做一个vue键盘组件

文章目录 1. 从0做一个键盘组件1.1. 最终效果1.2. 分析1.3. 实现1.4. 如何引用 1. 从0做一个键盘组件 首先是why的问题&#xff1a;为什么需要做键盘组件&#xff1f; 我们目前可知的场景&#xff1a; 在新增账单的时候&#xff0c;需要用到键盘在比如从账单列表页&#xff…

2024年 电工杯 (B题)大学生数学建模挑战赛 | 大学生平衡膳食食谱的优化设计 | 数学建模完整代码解析

DeepVisionary 每日深度学习前沿科技推送&顶会论文&数学建模与科技信息前沿资讯分享&#xff0c;与你一起了解前沿科技知识&#xff01; 本次DeepVisionary带来的是电工杯的详细解读&#xff1a; 完整内容可以在文章末尾全文免费领取&阅读&#xff01; 问题1&…

【Python自动化测试】:Unittest单元测试与HTMLTestRunner自动生成测试用例的好帮手

读者大大们好呀&#xff01;&#xff01;!☀️☀️☀️ &#x1f525; 欢迎来到我的博客 &#x1f440;期待大大的关注哦❗️❗️❗️ &#x1f680;欢迎收看我的主页文章➡️寻至善的主页 文章目录 &#x1f525;前言&#x1f680;unittest编写测试用例&#x1f680;unittest测…

49 序列化和反序列化

本章重点 理解应用层的作用&#xff0c;初识http协议 理解传输层的作用&#xff0c;深入理解tcp的各项特性和机制 对整个tcp/ip协议有系统的理解 对tcp/ip协议体系下的其他重要协议和技术有一定的了解 学会使用一些网络问题的工具和方法 目录 1.应用层 2.协议概念 3. 网络计…