十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

.NET 8 + Azure 登录 + Ant Design Blazor 企业身份认证实战

.NET 8 + Azure 登录 + Ant Design Blazor 企业身份认证实战 简介这是一套面向.NET开发者的后台管理框架案例基于.NET 8与Azure登录集成采用Ant Design Blazor构建主界面并考虑了常见后台管理场景。框架运行在Blazor Server模式下实现了菜单导航、路由跳转以及本地JSON文件的读取与修改适合需要快速搭建企业级后台系统、或希望系统学习Blazor与Azure认证集成的初中级.NET开发人员参考。资源包共含147个文件压缩包大小3.57MB其中62个dll程序集支撑运行依赖、26个json用于配置、9个cs与9个razor展示核心逻辑和页面结构另有css样式、svg图标、props工程文件等整体目录结构清晰。这些文件方便直接还原项目环境开展二次开发。目前已有280人学习下载通过该框架开发者可理清Azure登录流程与Blazor Server导航路由的配置方法掌握Ant Design Blazor组件的使用技巧并可直接复用JSON配置读写功能作为新项目的基础脚手架或技术验证样例。 最近接了个活要给一套企业内部工具加上统一的身份认证前端用 Blazor后端打算跑在 .NET 8 上。选型的时候没怎么犹豫认证直接走 Azure 登录Microsoft Entra IDUI 组件用了 Ant Design Blazor。这套组合跑通之后整体体验相当顺这里把完整的落地过程、踩坑记录和关键代码都整理出来给正在做类似选型的朋友一个参考。1. 项目背景与整体方案拆解1.1 为什么是 .NET 8 Azure 登录 Ant Design Blazor先说结论这三个东西组合在一起解决的是“从零搭一套带企业身份认证的现代化 Web 应用”这个刚需问题。.NET 8 是微软当前的主力 LTS 版本支持 Blazor WebAssembly 和 Blazor Server 两种托管模型。我这次用的是 Blazor WebAssemblyWasm因为前端需要较强的交互性而且部署环境允许纯静态托管可以跟后端 API 分开走。如果你对内网延迟敏感也可以考虑 Blazor Server但后文代码里大部分认证逻辑是通用的。Azure 登录也就是 Microsoft Entra ID老的 Azure AD企业场景里几乎绕不开。用户已经在 Microsoft 365 / Office 365 里有账号直接用它当身份源省掉了自建用户体系的麻烦。OAuth 2.0 / OpenID Connect 协议是标准流程.NET 官方库支持得很好。Ant Design Blazor 则是把 Ant Design 的设计语言搬到 Blazor 生态里。Ant Design 在 React 圈子里早就验证过了组件审美在线、交互齐全。Blazor 版本虽然没 React 那么成熟但常用的 Table、Form、Layout、Menu 等质量完全够用而且组件是 C# 写的跟前端逻辑不用来回切语言。这三个组合在一起最大的价值是“全栈 C#”前端 C#、后端 C#、认证走标准协议团队成员只要会 C# 就能完整把控整个链路不用引入第二门语言。1.2 这套方案适合什么样的项目如果你的项目符合下面几条那这套组合会很舒服用户身份已经存在于 Microsoft 365 / Entra ID 中希望实现单点登录SSO。团队以 .NET 技术栈为主不希望前端引入繁重的 JS 框架。需要一个开箱即用的中后台 UI 组件库Table、Form、布局都要好看且功能完整。需要对接 Azure 上的其他资源如 Azure OpenAI、Storage、Graph API登录凭证可以复用。反过来说如果你的用户是外部 C 端消费者没有微软账号体系那 Azure 登录就不是最优解建议直接走 ASP.NET Core Identity 外部登录提供商。2. Azure 登录的前置准备2.1 在 Azure 门户创建应用注册这个步骤是整套流程的“钥匙”注册错了后面全白搭。登录 Azure 门户打开“Microsoft Entra ID”进入“应用注册”点击“新注册”。这里有几个关键字段名称填个应用名比如MyBlazorApp对用户可见。受支持的帐户类型如果你只在企业内部用选“仅此组织目录中的帐户”如果还要支持微软个人账号或其他组织的账号选“任何组织目录中的帐户”。我这次是在企业内部选的第一项。重定向 URI这一步必须现在就填否则登录成功后会报AADSTS50011错误。平台选“单页应用程序 (SPA)”或“Web”URI 填写你的应用实际地址。Blazor WebAssembly 通常用 SPA 类型Blazor Server 用 Web 类型。我用的地址是https://localhost:7001/authentication/login-callback这是 Blazor 默认的回调路径端口需要跟你实际跑起来的一致。2.2 配置 API 权限与客户端凭据注册完成后进入“公开 API”或“API 权限”菜单给应用添加权限。如果你是纯前端登录不加受保护 API那只需要默认的User.Read读取用户基本信息就够了。但企业内部工具往往还要调用自己的 Web API此时需要在应用中暴露一个作用域Scope例如api://my-blazor-app/access_as_user。具体操作在“公开 API”中点击“添加范围”范围名称填access_as_user然后选择谁可以同意。接着在“API 权限”中“添加权限”切换到“我的 API”页签选中刚刚暴露的 Scope这样就完成了前后端的权限关联。还要注意“客户端凭据”如果前端是纯 SPA用授权码 PKCE 流程不需要客户端密码。微软推荐 SPA 不走 Authorization Code PKCE所以不需要创建 client secret。这一点很多资料会误导你去生成密钥其实没必要反而有泄露风险。3. Blazor 与 Ant Design 的整合细节3.1 建立 .NET 8 Blazor WebAssembly 项目我用的是 .NET 8 SDK直接用 dotnet CLI 创建项目dotnet new blazorwasm-empty -n BlazorAzureAntd -o . dotnet add package Microsoft.Authentication.WebAssembly.MSAL dotnet add package AntDesign dotnet add package Microsoft.AspNetCore.Components.WebAssembly.Authenticationblazorwasm-empty模板比带 UI 的模板干净不会有一堆示例代码干扰。加了包之后需要在Program.cs里注册服务using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using BlazorAzureAntd; using Microsoft.AspNetCore.Components.WebAssembly.Authentication; using AntDesign; var builder WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.AddApp(#app); builder.RootComponents.AddHeadOutlet(head::after); builder.Services.AddScoped(sp new HttpClient { BaseAddress new Uri(builder.HostEnvironment.BaseAddress) }); builder.Services.AddMsalAuthentication(options { builder.Configuration.Bind(AzureAd, options.ProviderOptions.Authentication); options.ProviderOptions.LoginMode redirect; options.ProviderOptions.DefaultAccessTokenScopes.Add(api://my-blazor-app/access_as_user); }); builder.Services.AddAntDesign(); await builder.Build().RunAsync();注意AddMsalAuthentication是微软提供的 MSAL 集成它自动处理了授权码流程和 token 的存储刷新。LoginMode我用的是 redirect 而不是 popup因为有些浏览器对弹窗拦截比较严格redirect 模式更稳。3.2 引入 AntDesign 样式与交互在index.html里加入 AntDesign 的静态资源link href_content/AntDesign/css/ant-design-blazor.css relstylesheet / script src_content/AntDesign/js/ant-design-blazor.js/script然后在根组件App.razor里包一层ConfigProvider可以设置全局语言和其他主题配置ConfigProvider CascadingAuthenticationState Router AppAssemblytypeof(App).Assembly Found ContextrouteData AuthorizeRouteView RouteDatarouteData DefaultLayouttypeof(MainLayout) / FocusOnNavigate RouteDatarouteData Selectorh1 / /Found NotFound LayoutView Layouttypeof(MainLayout) Result Status404 Title404 SubTitle页面不存在 / /LayoutView /NotFound /Router /CascadingAuthenticationState /ConfigProvider这里最核心的是CascadingAuthenticationState和AuthorizeRouteView它们负责把登录状态传下去并在未授权时触发跳转登录。4. 实现 Azure 登录后端与前端的关键代码4.1 配置 appsettings.json 里的 AzureAd 节点在wwwroot/appsettings.json中配置{ AzureAd: { Authority: https://login.microsoftonline.com/你的租户ID, ClientId: 你的应用客户端ID, ValidateAuthority: true } }一个坑点上面我是在Program.cs里用builder.Configuration.Bind(AzureAd, options.ProviderOptions.Authentication)绑定的。因为 Blazor WebAssembly 运行在浏览器里配置文件必须放在wwwroot下而不是项目根目录。很多人直接把appsettings.json放根目录结果运行时报找不到配置。另外你的“租户ID”可以在 Azure 门户的应用注册概览页找到是一串 GUID。4.2 登录组件与用户状态显示接下来做一个简单的登录/登出组件。我习惯在MainLayout的顶部导航栏放用户状态using Microsoft.AspNetCore.Components.Authorization using Microsoft.AspNetCore.Components.WebAssembly.Authentication inject NavigationManager Navigation inject AuthenticationStateProvider AuthProvider AuthorizeView Authorized Space Avatar Stylebackground-color:#1890ff Iconuser / spancontext.User.Identity?.Name/span Button Typeprimary Danger OnClickLogout退出/Button /Space /Authorized NotAuthorized Button Typeprimary OnClickLogin登录/Button /NotAuthorized /AuthorizeView code { private void Login() { Navigation.NavigateToLogin(authentication/login); } private void Logout() { Navigation.NavigateToLogout(authentication/logout); } }这里用了 Blazor 内置的AuthorizeView组件登录后context.User.Identity.Name是用户的主邮箱或 UPN可以直接展示。NavigateToLogin和NavigateToLogout是扩展方法来自Microsoft.AspNetCore.Components.WebAssembly.Authentication命名空间它们会自动拼接完整的 OAuth 登录/登出地址。4.3 调用受保护 API 的 Token 处理纯登录型应用太少见了多数场景是要带上 token 去调用后端 API。Blazor WebAssembly 里可以用AccessTokenProvider拿到 token然后手动塞进 HttpClient 的 Authorization 头using Microsoft.AspNetCore.Components.WebAssembly.Authentication; public class ApiService : IApiService { private readonly HttpClient _http; private readonly IAccessTokenProvider _tokenProvider; public ApiService(HttpClient http, IAccessTokenProvider tokenProvider) { _http http; _tokenProvider tokenProvider; } public async Taskstring GetDataAsync() { var tokenResult await _tokenProvider.RequestAccessToken(); if (tokenResult.TryGetToken(out var token)) { var request new HttpRequestMessage(HttpMethod.Get, api/data); request.Headers.Authorization new System.Net.Http.Headers.AuthenticationHeaderValue(Bearer, token.Value); var response await _http.SendAsync(request); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(); } throw new InvalidOperationException(无法获取访问令牌); } }注意TryGetToken如果返回 false说明 token 刷新失败或用户已退出这时候应该引导用户重新登录而不是默默返回空数据。后端 API 这边如果你是用 .NET 8 的 Minimal API 或 Controller需要配置 JWT Bearer 认证校验 token 的 issuer 和 audience。最简单的做法是加一个Microsoft.Identity.Web包它跟 Azure 的集成度最高dotnet add package Microsoft.Identity.Web然后在后端Program.cs里builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection(AzureAd)); builder.Services.AddAuthorization();后端配置的AzureAd节点里要有ClientId、TenantId以及刚才在前端暴露的 Scope 对应的Audience比如api://my-blazor-app。这样前端拿到的 token 才能通过后端的鉴权验证。5. 常见问题与排查技巧我在这次开发中至少踩了下面这几个坑有些问题查了挺久才定位到原因整理成表格给大家参考。异常信息原因分析解决办法AADSTS50011: The reply URL specified in the request does not match重定向 URI 没注册或填错常见于端口号变化登录 Azure 门户在应用注册的“重定向 URI”中加入当前实际地址LoginError: No account found with the requested ID缓存了旧的登录账号或租户 ID 配置错误清除浏览器缓存 / 退出所有账号后重新登录检查 appsettings 中租户 ID 是否属于该应用所在目录Failed to load resource: ... 401后端 API 没有配置 JWT 认证或 audience 不一致后端加.AddMicrosoftIdentityWebApi确认 Scope 的api://前缀和前端 DefaultAccessTokenScopes 一致Ant Design 组件无样式缺少 CSS 引用或静态资源路径不对检查index.html中是否引入ant-design-blazor.cssJS 互操作报错AntDesign 的 JS 文件未加载或加载顺序靠后确保script src_content/AntDesign/js/ant-design-blazor.js/script放在app.js之前Unauthorised但 token 拿到了后端校验的 issuer/audience 不匹配用 jwt.ms 解码 token对比前端和后端配置的 ClientId、TenantId 是否一致5.1 一个典型的 AADSTS50011 排查过程举个例子我第一次配重定向 URI 时没注意 IIS Express 会随机更换端口导致每次重启端口都在变。后来干脆固定了启动端口在Properties/launchSettings.json里设置applicationUrl: https://localhost:7001然后把7001端口提前注册到 Azure 门户。这样不管怎么重启回调地址都是稳定的。如果你还是遇到 50011优先检查回调 URL 的 protocol、host、port 是否与代码里导航到的完全一致连末尾的斜杠都不能差。5.2 交互卡在登录页循环跳转还有一次登录流程陷入了“登录 → 回调 → 又跳转登录”的死循环。排查后发现是AuthorizationMessageHandler配置了自定义的 HttpClient但这个 handler 默认会拼接defaultAccessTokenScopes如果 API 的作用域和前端配置的 scope 不一致token 请求失败于是始终拿不到用户身份。解决方法是给DefaultAccessTokenScopes指定明确的 scope 字符串并保证后端注册的 API 作用域完全匹配。如果不确定可以先在登录后把 token 打印出来在 https://jwt.ms 上解码查看aud和scp字段再对照后端配置。6. 实操心得与补充建议这套 .NET 8 Azure 登录 Ant Design Blazor 的组合我跑下来整体是稳的但也有几个地方想单独多说一句。6.1 版本兼容性要小心Ant Design Blazor 对 .NET 8 的支持是从 0.15 版本开始的早期 0.14 以下版本可能只适配 .NET 6/7。建议直接用 NuGet 上的最新稳定版我写这篇时 1.0 正式版已经发布API 和旧版有一些差异网上很多旧教程的代码可能跑不起来。如果你从旧项目升级重点检查Table的Data参数、Form的LabelCol/WrapperCol是否改成子组件写法。6.2 托管方式影响很大如果是 Blazor WebAssembly 模式发布后是静态文件可以放任意 CDN。但 Azure 登录要求回调地址必须是 HTTPS所以本地调试要开 HTTPS生产环境也别忘了绑定证书。如果是 Blazor Server 模式登录流程会走服务端很多配置和端上模式不同不要硬套本文代码。6.3 多租户场景提前规划如果应用要被多个组织的 Microsoft 账号登录Azure 应用注册里的“支持的帐户类型”要选择“任何组织目录”代码里Authority要从租户 ID 改为common或organizations。这样能省去后续改架构的麻烦。但多租户场景下 token 的验证逻辑更复杂需要用tenantid字段做租户隔离这块务必提前设计。6.4 别忘了用户体验登录体验上Ant Design Blazor 的Spin和Result组件很好用。在登录跳转的过程中我会在根页面放一个全屏Spin遮罩避免用户误以为页面卡住。另外登录失败的页面用Result组件展示具体错误码方便用户自行反馈而不是白屏。最后分享一个我自己的小习惯Azure 应用注册里每个环境开发、测试、生产单独建一个应用注册虽然多了几步操作但环境间配置隔离明确不会出现改了测试环境配置导致生产登录异常的问题。个人项目的认证配置看似简单一旦环境多了混乱是必然的提前隔离能省很多心。本文还有配套的精品资源点击获取
返回列表