使用 Azure Active Directory 保护 ASP.NET Core Blazor WebAssembly 独立应用Secure an ASP.NET Core Blazor WebAssembly standalone app with Azure Active Directory

本文内容

作者: Javier Calvarro 使用Luke Latham

重要

Blazor WebAssembly 为预览版状态

ASP.NET Core 3.0 支持 Blazor Server。Blazor WebAssembly 在 ASP.NET Core 3.1 中为预览版。

备注

本文中的指导适用于 ASP.NET Core Blazor WebAssembly 模板 3.2 版或更高版本。要在未使用 Visual Studio 版本 16.6 预览版 2 或更高版本时获取最新的 Blazor WebAssembly 模板(版本 3.2.0-preview3.20168.3),请参阅 ASP.NET Core Blazor 入门

创建使用Azure Active Directory (AAD)进行身份验证的 Blazor WebAssembly 独立应用程序:

创建 AAD 租户和 web 应用程序

在 Azure 门户的Azure Active Directory > 应用注册区域中注册 AAD 应用:

  • 提供应用的名称(例如, Blazor 客户端 AAD)。
  • 选择受支持的帐户类型。你只能在此组织目录中选择帐户
  • 将 "重定向 uri " 下拉集保持设置为 " Web",并提供 https://localhost:5001/authentication/login-callback的重定向 uri。
  • 禁用 "权限 > 向 openid 和 offline_access 权限授予管理员权限" 复选框。
  • 选择“注册”。
    在 "身份验证" > 平台配置 > Web

  • 确认存在 https://localhost:5001/authentication/login-callback重定向 URI

  • 对于 "隐式授予",选中 "访问令牌" 和 " ID 令牌" 对应的复选框。
  • 此体验可接受应用的其余默认值。
  • 选择“保存”按钮。
    记录以下信息:
  • 应用程序 ID (客户端 ID)(例如 11111111-1111-1111-1111-111111111111
  • 目录 ID (租户 ID)(例如 22222222-2222-2222-2222-222222222222

将以下命令中的占位符替换为前面记录的信息,然后在命令行界面中执行命令:

  1. dotnet new blazorwasm -au SingleOrg --client-id "{CLIENT ID}" --tenant-id "{TENANT ID}"

若要指定输出位置(如果它不存在,则创建一个项目文件夹),请在命令中包含 output 选项,其中包含一个路径(例如 -o BlazorSample)。文件夹名称还会成为项目名称的一部分。

身份验证包Authentication package

创建应用以使用工作或学校帐户(SingleOrg)时,应用会自动接收Microsoft 身份验证库Microsoft.Authentication.WebAssembly.Msal)的包引用。包提供一组基元,可帮助应用对用户进行身份验证,并获取令牌以调用受保护的 Api。

如果向应用程序中添加身份验证,请将包手动添加到应用的项目文件中:

  1. <PackageReference Include="Microsoft.Authentication.WebAssembly.Msal"
  2. Version="{VERSION}" />

将前面的包引用中的 {VERSION} 替换为 ASP.NET Core Blazor 入门 一文中所示 Microsoft.AspNetCore.Blazor.Templates 包的版本。

Microsoft.Authentication.WebAssembly.Msal 包可向应用程序中添加 Microsoft.AspNetCore.Components.WebAssembly.Authentication 包。

身份验证服务支持Authentication service support

使用 Microsoft.Authentication.WebAssembly.Msal 包提供的 AddMsalAuthentication 扩展方法在服务容器中注册对用户进行身份验证。此方法设置应用程序与标识提供程序(IP)交互所需的所有服务。

Program.cs:

  1. builder.Services.AddMsalAuthentication(options =>
  2. {
  3. var authentication = options.ProviderOptions.Authentication;
  4. authentication.Authority = "https://login.microsoftonline.com/{TENANT ID}";
  5. authentication.ClientId = "{CLIENT ID}";
  6. });

AddMsalAuthentication 方法接受回调,以配置对应用进行身份验证所需的参数。注册应用时,可以从 Azure 门户 AAD 配置获取配置应用所需的值。

Blazor WebAssembly 模板不会自动配置应用以请求安全 API 的访问令牌。若要将令牌预配为登录流的一部分,请将该作用域添加到 MsalProviderOptions的默认访问令牌作用域中:

  1. builder.Services.AddMsalAuthentication(options =>
  2. {
  3. ...
  4. options.ProviderOptions.DefaultAccessTokenScopes.Add(
  5. "{SERVER API APP CLIENT ID}/{DEFAULT SCOPE}");
  6. });

备注

默认访问令牌范围必须是 {SERVER API APP CLIENT ID}/{DEFAULT SCOPE} 格式(例如,11111111-1111-1111-1111-111111111111/API.Access)。如果向范围设置提供方案或方案和主机(如 Azure 门户中所示),则当客户端应用收到来自服务器 API 应用401 未经授权响应时,将引发未处理的异常。

索引页面Index page

"索引页(wwwroot/index.html)" 页包含一个用于在 JavaScript 中定义 AuthenticationService 的脚本。AuthenticationService 处理 OIDC 协议的低级别详细信息。应用在内部调用在脚本中定义的方法来执行身份验证操作。

  1. <script src="_content/Microsoft.Authentication.WebAssembly.Msal/
  2. AuthenticationService.js"></script>

应用组件App component

App 组件(app.config)类似于在 Blazor Server apps 中找到 App 组件:

  • CascadingAuthenticationState 组件管理向应用程序的其余部分公开 AuthenticationState
  • AuthorizeRouteView 组件确保当前用户有权访问给定页面或呈现 RedirectToLogin 组件。
  • RedirectToLogin 组件管理将未经授权的用户重定向到登录页。
  1. <CascadingAuthenticationState>
  2. <Router AppAssembly="@typeof(Program).Assembly">
  3. <Found Context="routeData">
  4. <AuthorizeRouteView RouteData="@routeData"
  5. DefaultLayout="@typeof(MainLayout)">
  6. <NotAuthorized>
  7. <RedirectToLogin />
  8. </NotAuthorized>
  9. </AuthorizeRouteView>
  10. </Found>
  11. <NotFound>
  12. <LayoutView Layout="@typeof(MainLayout)">
  13. <p>Sorry, there's nothing at this address.</p>
  14. </LayoutView>
  15. </NotFound>
  16. </Router>
  17. </CascadingAuthenticationState>

RedirectToLogin 组件RedirectToLogin component

RedirectToLogin 组件(Shared/RedirectToLogin):

  • 管理将未经授权的用户重定向到登录页。
  • 保留用户尝试访问的当前 URL,以便在身份验证成功时可以将其返回到该页。
  1. @inject NavigationManager Navigation
  2. @using Microsoft.AspNetCore.Components.WebAssembly.Authentication
  3. @code {
  4. protected override void OnInitialized()
  5. {
  6. Navigation.NavigateTo($"authentication/login?returnUrl={Navigation.Uri}");
  7. }
  8. }

LoginDisplay 组件LoginDisplay component

LoginDisplay 组件(shared/LoginDisplay)在 MainLayout 组件(shared/MainLayout)中呈现并管理以下行为:

  • 对于经过身份验证的用户:
    • 显示当前用户名。
    • 提供用于注销应用的按钮。
  • 对于匿名用户,提供登录选项。
  1. @using Microsoft.AspNetCore.Components.Authorization
  2. @using Microsoft.AspNetCore.Components.WebAssembly.Authentication
  3. @inject NavigationManager Navigation
  4. @inject SignOutSessionStateManager SignOutManager
  5. <AuthorizeView>
  6. <Authorized>
  7. Hello, @context.User.Identity.Name!
  8. <button class="nav-link btn btn-link" @onclick="BeginSignOut">
  9. Log out
  10. </button>
  11. </Authorized>
  12. <NotAuthorized>
  13. <a href="authentication/login">Log in</a>
  14. </NotAuthorized>
  15. </AuthorizeView>
  16. @code {
  17. private async Task BeginSignOut(MouseEventArgs args)
  18. {
  19. await SignOutManager.SetSignOutState();
  20. Navigation.NavigateTo("authentication/logout");
  21. }
  22. }

身份验证组件Authentication component

Authentication 组件(Pages/Authentication)生成的页面定义处理不同的身份验证阶段所需的路由。

RemoteAuthenticatorView 组件:

  • Microsoft.AspNetCore.Components.WebAssembly.Authentication 包提供。
  • 管理在每个身份验证阶段执行适当的操作。
  1. @page "/authentication/{action}"
  2. @using Microsoft.AspNetCore.Components.WebAssembly.Authentication
  3. <RemoteAuthenticatorView Action="@Action" />
  4. @code {
  5. [Parameter]
  6. public string Action { get; set; }
  7. }

疑难解答Troubleshoot

由于 ID 令牌和访问令牌可在登录尝试期间保持,因此,每次更新后,请使用浏览器的开发人员控制台清除浏览器 cookie:

  • 应用的身份验证代码或配置设置。
  • 应用的配置 OIDC 兼容的提供程序(例如 Azure Active Directory)。

其他资源Additional resources