
在 Blazor WebAssembly 中集成 MSALMicrosoft.Authentication.WebAssembly.Msal 包实战与源码解析【免费下载链接】aspnetcoreASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.项目地址: https://gitcode.com/GitHub_Trending/as/aspnetcoreMicrosoft.Authentication.WebAssembly.Msal是 ASP.NET Core 仓库中面向Blazor WebAssembly客户端渲染应用的认证包它封装了微软的 MSAL.js 库为运行在浏览器沙箱中的 Blazor 应用提供基于Azure Active Directory / Azure AD B2C的纯客户端登录能力。本文以该包的官方说明PACKAGE.md为骨架结合仓库内真实源码与项目模板完整讲解安装方式、AddMsalAuthentication接入方法、MsalProviderOptions/MsalAuthenticationOptions/MsalCacheOptions三大配置模型的全部选项与默认值并深入解释默认值如何被后置配置器落地执行。读完你将能独立在 WASM 应用中完成从安装、配置到登录交互组件的全套接入并理解底层配置机制。包定位为什么 Blazor WebAssembly 需要 MSALBlazor WebAssembly 应用的所有代码包括认证逻辑都在浏览器端运行无法像 Blazor Server 那样复用服务端会话与 Cookie 认证中间件。因此 .NET 团队以 MSAL.js 为底层引擎提供了一组面向客户端的“远程认证”封装其中针对微软 Azure AD / Azure AD B2C 身份源的就是本包。从仓库结构可以清晰看到它的组成目录 src/Components/WebAssembly/Authentication.Msal/srcC# 侧MsalWebAssemblyServiceCollectionExtensions.csDI 扩展入口、三个 Models 文件选项模型、MsalDefaultOptionsConfiguration.cs默认值后置配置器JS 互操作侧Interop/AuthenticationService.ts 封装 MSAL.js 调用通过 JSInterop 供 .NET 侧驱动登录、登出与令牌获取配套的 ILLink.Descriptors.xml 用于在发布裁剪trimming时保留上述选项类型说明该包在设计上即面向 WebAssembly 裁剪后的精简运行时。需要说明该包仅负责客户端侧认证流程。若需要校验由它签发的令牌服务端托管该 WASM 应用的 ASP.NET Core 宿主还需配合 JWT Bearer 认证来验证令牌二者分工不同。安装一条 dotnet 命令包说明给出的安装方式非常直接dotnet add package Microsoft.Authentication.WebAssembly.Msal该命令会把包引用写入项目文件.csproj。由于 Blazor WebAssembly 应用需要调用浏览器中的 MSAL.js 脚本安装包后会一并带上经过打包的 JS 资源对应仓库中的 Interop 目录及其package.json/rollup.config.mjs构建产物应用只需正常发布即可携带这些静态资源无需手工下载任何脚本。注意本包只适用于Microsoft.AspNetCore.Components.WebAssembly客户端项目。Blazor Server 场景请使用服务端侧的认证方案若是 Azure AD B2C 且希望借用微软提供的基础 UI 流程也仍以本包配合RemoteAuthenticatorView使用。接入从 AddMsalAuthentication 说起包说明将具体用法指向官方安全文档而其编程入口在本仓库的 MsalWebAssemblyServiceCollectionExtensions.cs 中定义。该文件提供了三个泛型层层递进的重载// 1) 最简形式使用默认 RemoteAuthenticationState 与 RemoteUserAccount services.AddMsalAuthentication(options { builder.Configuration.Bind(AzureAd, options.ProviderOptions.Authentication); }); // 2) 自定义认证状态类型 services.AddMsalAuthenticationRemoteAuthenticationState(options { /* ... */ }); // 3) 同时自定义状态类型与用户账户类型 services.AddMsalAuthenticationTRemoteAuthenticationState, TAccount(options { /* ... */ });其中带泛型的两个重载分别在类型参数上标注了[DynamicallyAccessedMembers(JsonSerialized)]并要求TRemoteAuthenticationState : RemoteAuthenticationState, new()、TAccount : RemoteUserAccount——这是为了让裁剪器知道这些类型会被 JSON 反序列化避免发布后被剪掉导致状态还原失败。从源码看无论走哪个重载最终都会落到第三个泛型实现L54-L64其内部只做两件事调用AddRemoteAuthenticationTRemoteAuthenticationState, TAccount, MsalProviderOptions(configure)注册通用的远程认证基础设施通过TryAddEnumerable注册MsalDefaultOptionsConfiguration作为IPostConfigureOptionsRemoteAuthenticationOptionsMsalProviderOptions用于兜底填充默认值。AddRemoteAuthentication本身来自同仓库的基础认证抽象包Microsoft.AspNetCore.Components.WebAssembly.Authentication可见 MSAL 集成是建立在通用的“远程认证”框架之上的认证提供方被抽象为MsalProviderOptionsUI 层使用通用的AuthorizeView、AuthorizeRouteView与RemoteAuthenticatorView这也是为什么即便更换身份提供商如切换到 OIDCBlazor 组件代码可以保持一致。配置模型一MsalProviderOptionsProviderOptionsMsalProviderOptionsModels/MsalProviderOptions.cs是传给 MSAL.js 的顶层配置也是options.ProviderOptions的类型。它包含五个成员成员类型默认值说明AuthenticationMsalAuthenticationOptionsRedirectUri与PostLogoutRedirectUri预置见下节JSON 序列化名称为auth对应 MSAL.js 的 auth 配置块CacheMsalCacheOptionsCacheLocation sessionStorageStoreAuthStateInCookie false对应 MSAL.js 的 cache 配置块DefaultAccessTokenScopesIListstring空集合登录流程中默认申请的访问令牌作用域AdditionalScopesToConsentIListstring空集合首次登录时额外征求同意的作用域用于跨资源授权LoginModestringpopup发起登录的交互模式几个要点LoginMode 默认是popup弹出窗口方式而非整页重定向。若你的应用期望以redirect方式登录例如需要精确控制页面跳转或被嵌入 iframe可改为redirect。DefaultAccessTokenScopes与AdditionalScopesToConsent的区别很实际前者是登录成功后直接为已配置的 API 预取令牌后者只是在同意页上一并征求其他资源的作用域许可二者配合可显著减少后续静默获取令牌时的额外交互。其中Authentication属性带有[JsonPropertyName(auth)]Cache也按 msal.js 的配置结构命名说明这些选项最终会被逐项映射为 MSAL.js 构造参数保持与上游库一致的语义。配置模型二MsalAuthenticationOptionsAuthenticationMsalAuthenticationOptionsModels/MsalAuthenticationOptions.cs描述 MSAL.js 的 auth 块是最常被配置的对象典型的 appsettings.json 绑定写法如下{ AzureAd: { Authority: https://login.microsoftonline.com/{tenantId}, ClientId: {client-id}, ValidateAuthority: true } }builder.Services.AddMsalAuthentication(options { builder.Configuration.Bind(AzureAd, options.ProviderOptions.Authentication); });各属性语义与默认值如下属性默认值说明ClientIdnull必填Azure AD / B2C 中应用注册的客户端 IDApplication / Client IDAuthoritynullAzure AD 或 Azure AD B2C 实例的颁发机构地址。B2C 场景通常为https://{tenant}.b2clogin.com/{tenant}/{policy}形式ValidateAuthoritytrue是否校验 authority使用 Azure AD B2C 时需显式置为falseRedirectUri相对路径authentication/login-callback登录回跳地址可为绝对或基于基地址的相对 URIPostLogoutRedirectUri相对路径authentication/logout-callback登出后回跳地址同样支持相对或绝对 URINavigateToLoginRequestUrlfalse由配置器强制登录成功后是否回到发起登录时的 URLKnownAuthorities空集合已知的 authority 主机名列表用于 B2C 等多租户/自定义域场景辅助校验其中ValidateAuthority false的 B2C 约束、RedirectUri/PostLogoutRedirectUri相对路径默认值都直接对应源码中的注释与初始化见下节配置器实现。配置模型三MsalCacheOptionsCacheMsalCacheOptionsModels/MsalCacheOptions.cs控制令牌缓存的存放位置属性默认值说明CacheLocationsessionStorage令牌缓存存放位置合法值为sessionStorage或localStorageStoreAuthStateInCookiefalse是否同时把认证状态写入 Cookie默认值sessionStoragefalse与 MSAL.js 自身的默认保持一致源码注释中特别注明 “This matches the defaults in msal.js”。二者组合意味着令牌仅存活于当前浏览器标签页会话关闭标签页即失效安全性较高但每次新开标签页都需要重新认证若希望跨标签页保持登录态可将CacheLocation调整为localStorage。默认值背后的执行逻辑MsalDefaultOptionsConfiguration虽然MsalProviderOptions在类型定义里就带了默认值但还有一部分依赖运行时环境如基地址的默认值必须“后置处理”。这正是MsalDefaultOptionsConfigurationMsalDefaultOptionsConfiguration.cs的职责——它实现了IPostConfigureOptionsRemoteAuthenticationOptionsMsalProviderOptions注入NavigationManager获取应用当前基地址。其Configure方法共做四件事对应 L20-L42// 1) 用户标识中的 scope 声明默认取 scpBearer 令牌格式 options.UserOptions.ScopeClaim ?? scp; // 2) 认证类型默认取 ClientId options.UserOptions.AuthenticationType ?? options.ProviderOptions.Authentication.ClientId; // 3) RedirectUri 为空或相对路径时基于当前基地址解析为绝对 URI // 默认相对路径为 authentication/login-callback if (redirectUri null || !Uri.TryCreate(redirectUri, UriKind.Absolute, out _)) { redirectUri ?? authentication/login-callback; options.ProviderOptions.Authentication.RedirectUri _navigationManager.ToAbsoluteUri(redirectUri).AbsoluteUri; } // 4) PostLogoutRedirectUri 同理默认 authentication/logout-callback options.ProviderOptions.Authentication.NavigateToLoginRequestUrl false;由此可以回答几个常见的“为什么”为什么 appsettings.json 里写RedirectUri: authentication/login-callback也能正常工作因为该配置器发现它不是绝对 URI 后会用NavigationManager.ToAbsoluteUri拼出完整的应用内地址自动适配部署路径无需手写完整域名为什么NavigateToLoginRequestUrl即使你配置为true也会被关掉因为配置器在IPostConfigureOptions阶段无条件赋值false——需要说明赋值发生在后置阶段若你在configure回调中先配置了true仍会被此覆盖从当前源码看该行为是强制的ScopeClaim 为什么要默认scp微软签发访问令牌中的作用域声明名为scp将其作为UserOptions.ScopeClaim的默认值才能让下游IAccessTokenProvider/授权逻辑正确解析令牌内的作用域。由于它继承自RemoteAuthenticationOptionsMsalProviderOptions的认证状态基类RedirectUri等参数要求是绝对地址或基地址相对地址这也解释了为何配置文件里常见的写法是纯相对路径字符串——最终都会在这里被补全为NavigationManager解析出的绝对地址。同时注意Configure与PostConfigure两方法的配合PostConfigure只在 name 为默认名Options.DefaultName时触发确保多实例配置场景下默认值兜底只作用于默认命名实例。组装进应用Program.cs 中的完整接入形态综合上述配置模型在一个最小 Blazor WebAssembly 应用中接入 Azure AD 认证的完整代码如下与此仓库项目模板 ComponentsWebAssembly-CSharp 模板 生成的结构一致using Microsoft.AspNetCore.Components.WebAssembly.Authentication; using Microsoft.Authentication.WebAssembly.Msal; var builder WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.AddApp(#app); builder.RootComponents.AddHeadOutlet(head::after); // 1) 注册 MSAL 认证配置项从 appsettings.json 的 AzureAd 节读取 builder.Services.AddMsalAuthentication(options { builder.Configuration.Bind(AzureAd, options.ProviderOptions.Authentication); // 可选为下游 API 预取令牌 options.ProviderOptions.DefaultAccessTokenScopes.Add(api://{api-app-id}/access_as_user); // 可选如需静默令牌刷新以外的交互方式 // options.ProviderOptions.LoginMode redirect; }); await builder.Build().RunAsync();配套的 UI 结构来自模板源码包括认证路由与回跳在 Pages/Authentication.razor 中暴露/authentication/{action}路由并渲染RemoteAuthenticatorView ActionAction /负责承接 login、login-callback、logout、logged-out 等动作登录入口由 Layout/LoginDisplay.razor 结合AuthorizeView显示“登录/用户信息/登出”未登录重定向通过 Layout/RedirectToLogin.razor 在用户访问受保护页面时跳转登录路由级授权在 App.razor 中以CascadingAuthenticationStateAuthorizeRouteView包裹路由并在NotAuthorized片段中触发上述重定向。模板中Home.razor还带有一段“在 Program.cs 中配置身份提供商详情前认证不会生效”的提示逻辑与AddOidcAuthenticationOIDC 分支并列存在——当工程选择微软账户作为身份源时实际生成的就是AddMsalAuthentication分支且Program.Main.cs中通过编译期条件如IndividualLocalAuth决定采用哪一套注册代码。发布与裁剪注意事项由于 Blazor WebAssembly 默认在发布时会对托管程序集做裁剪trimming而MsalProviderOptions、MsalCacheOptions、MsalAuthenticationOptions属于由 JSON 反序列化和 JS 互操作按名称反射访问的类型必须防止被裁剪器移除。仓库为此提供了 ILLink.Descriptors.xml以 XML 描述符的形式对这三个类型声明preservealllinker assembly fullnameMicrosoft.Authentication.WebAssembly.Msal type fullnameMicrosoft.Authentication.WebAssembly.Msal.Models.MsalProviderOptions preserveall / type fullnameMicrosoft.Authentication.WebAssembly.Msal.Models.MsalCacheOptions preserveall / type fullnameMicrosoft.Authentication.WebAssembly.Msal.MsalAuthenticationOptions preserveall / /assembly /linker这从侧面印证三个选项模型是序列化/互操作的关键契约。若你在自己的代码中扩展了自定义选项类型并希望其在裁剪后仍可用同样需要以[DynamicallyAccessedMembers]标注或提供描述符——这也是 MsalWebAssemblyServiceCollectionExtensions.cs 中泛型参数带JsonSerialized约束的原因。版本与配套基础包该包依赖 Blazor 的远程认证基础设施与 MSAL.js 运行时属于Microsoft.AspNetCore.App共享框架之外的独立 NuGet 包当前仓库中发布产物对应版本请以实际安装时 NuGet 解析结果为准。涉及同一类问题的邻近包还包括面向 OIDC非微软身份源的Microsoft.AspNetCore.Components.WebAssembly.Authentication系列。选择依据很简单身份源是Azure AD / Azure AD B2C用本包是其他 OIDC 提供方则使用通用的 OIDC 认证封装。从调试角度可观察浏览器 DevTools 中sessionStorage里以msal.开头的缓存键以及登录/登出回调时页面在/authentication/login-callback与/authentication/logout-callback之间的跳转——这两处路由与缓存位置正是本文所述默认值CacheLocation sessionStorage、相对回调路径在运行时的直接体现。小结Microsoft.Authentication.WebAssembly.Msal的接入路径可以概括为一条链路AddMsalAuthentication(options ...)MsalWebAssemblyServiceCollectionExtensions.cs→ 注册通用远程认证 注册MsalDefaultOptionsConfiguration后置配置器 →MsalProviderOptions含Authentication/Cache/作用域/登录模式被规范化相对回调地址补全为绝对地址、ScopeClaimscp、关闭NavigateToLoginRequestUrl→ 通过 Interop/AuthenticationService.ts 驱动 MSAL.js 完成 popup/redirect 登录 → 配合模板中的RemoteAuthenticatorView、AuthorizeRouteView、LoginDisplay完成完整 UI 闭环。掌握了安装命令、三大选项模型MsalProviderOptions/MsalAuthenticationOptions/MsalCacheOptions各自的默认值与适用场景再理解了默认值后置配置器的工作原理你就可以在 Blazor WebAssembly 应用中自如地接入 Azure AD / Azure AD B2C 登录并能在登录模式、缓存位置、令牌作用域等关键点上做出符合业务需要的取舍。【免费下载链接】aspnetcoreASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.项目地址: https://gitcode.com/GitHub_Trending/as/aspnetcore创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考