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

比如上图配置了两套不同的登录方案,各有自己的 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);
    }

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值