使用 Azure Active Directory B2C 保护 ASP.NET Core Blazor WebAssembly 托管应用Secure an ASP.NET Core Blazor WebAssembly hosted app with Azure Active Directory B2C

重要

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 模板(版本 ),请参阅 ASP.NET Core Blazor 入门

本文介绍如何创建使用进行身份验证的 Blazor WebAssembly 独立应用程序。

创建租户Create a tenant

按照中的指导创建 AAD B2C 租户并记录以下信息:

注册服务器 API 应用Register a server API app

遵循教程:在 Azure Active Directory B2C 中注册应用程序中的指导,在 Azure 门户的Azure Active Directory > 应用注册区域注册服务器 API 应用的 AAD 应用:

  • 选择“新注册”。
  • 提供应用的名称(例如 Blazor Server AAD B2C)。
  • 对于支持的帐户类型,请选择任何组织目录或任何标识提供者中的帐户。用于对具有 Azure AD B2C 的用户进行身份验证。(多租户)。
  • 在这种情况下,服务器 API 应用不需要重定向 uri ,因此请将下拉集设置为 " Web ",并不要输入 "重定向 uri"。
  • 确认权限 > 向 openid 授予 "管理员以免" 和 "offline_access" 权限已启用。
  • 选择“注册”。
    在中公开 API

  • 选择“添加范围”。

  • 选择“保存并继续”。
  • 提供作用域名称(例如 API.Access)。
  • 提供管理员同意显示名称(例如 Access API)。
  • 提供管理员同意说明(例如 Allows the app to access server app API endpoints.)。
  • 确认 "状态" 设置为 "已启用"。
  • 选择 "添加作用域"。
    记录以下信息:
  • 服务器 API 应用应用程序 ID (客户端 ID)(例如 11111111-1111-1111-1111-111111111111
  • 目录 ID (租户 ID)(例如 222222222-2222-2222-2222-222222222222
  • 服务器 API 应用应用 ID URI (例如 ,Azure 门户可能会将值默认为客户端 ID)
  • 默认作用域(例如 API.Access

注册客户端应用Register a client app

请按照中的指导,在 Azure 门户的Azure Active Directory > 应用注册区域中注册客户端应用的 AAD 应用:

  • 选择“新注册”。
  • 提供应用的名称(例如, Blazor 客户端 AAD B2C)。
  • 对于支持的帐户类型,请选择任何组织目录或任何标识提供者中的帐户。用于对具有 Azure AD B2C 的用户进行身份验证。(多租户)。
  • 将 "重定向 uri " 下拉集保持设置为 " Web",并提供 https://localhost:5001/authentication/login-callback的重定向 uri。
  • 确认权限 > 向 openid 授予 "管理员以免" 和 "offline_access" 权限已启用。
  • 选择“注册”。
    在 "身份验证" > 平台配置 > Web

  • 确认存在 重定向 URI

  • 对于 "隐式授予",选中 "访问令牌" 和 " ID 令牌" 对应的复选框。
  • 此体验可接受应用的其余默认值。
  • 选择“保存”按钮。
    在 " API 权限

  • 确认应用程序已Microsoft Graph > 用户。读取权限。

  • 选择 "添加权限",然后选择 "我的 api"
  • 从 "名称" 列中选择 "服务器 API 应用" (例如, Blazor server AAD B2C")。
  • 打开API列表。
  • 启用对 API 的访问(例如 API.Access)。
  • 选择“添加权限”。
  • 选择 "为 {租户名称} 授予管理内容" 按钮。请选择“是”以确认。
    Home > Azure AD B2C > 用户流

创建注册和登录用户流

记录以下信息:

  • 记录客户端应用应用程序 Id (客户端 id)(例如 33333333-3333-3333-3333-333333333333)。
  • 记录为应用创建的注册和登录用户流名称(例如 B2C_1_signupsignin)。

创建应用Create the app

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

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

服务器应用配置Server app configuration

本部分适用于解决方案的服务器应用。

Microsoft.AspNetCore.Authentication.AzureAD.UI提供对 ASP.NET Core Web Api 的身份验证和授权调用的支持:

  1. <PackageReference Include="Microsoft.AspNetCore.Authentication.AzureAD.UI"

身份验证服务支持Authentication service support

AddAuthentication 方法在应用中设置身份验证服务,并将 JWT 持有者处理程序配置为默认身份验证方法。AddAzureADBearer 方法在验证 Azure Active Directory 发出的令牌所需的 JWT 持有者处理程序中设置特定参数:

  1. services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
  2. .AddAzureADBearer(options => Configuration.Bind("AzureAd", options));

UseAuthenticationUseAuthorization 确保:

  • 应用尝试分析和验证传入请求的令牌。
  • 任何试图访问受保护资源的请求均不正确。
  1. app.UseAuthentication();
  2. app.UseAuthorization();

应用设置App settings

Appsettings文件包含用于配置用于验证访问令牌的 JWT 持有者处理程序的选项。

  1. {
  2. "AzureAd": {
  3. "Instance": "https://{ORGANIZATION}.b2clogin.com/",
  4. "ClientId": "{API CLIENT ID}",
  5. "Domain": "{DOMAIN}",
  6. "SignUpSignInPolicyId": "{SIGN UP OR SIGN IN POLICY}"
  7. }
  8. }

WeatherForecast 控制器WeatherForecast controller

WeatherForecast 控制器(控制器/WeatherForecastController)公开受保护的 API,并将 [Authorize] 特性应用到控制器。务必要了解:

  • 此 API 控制器中的 [Authorize] 属性是保护此 API 不受未经授权的访问的唯一操作。
  • Blazor WebAssembly 应用程序中使用的 [Authorize] 属性仅作为对应用程序的提示,用户应授权该应用程序正常工作。

本部分适用于解决方案的客户端应用。

身份验证包Authentication package

创建应用以使用单个 B2C 帐户(IndividualB2C)时,应用会自动接收(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 包。

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

Program.cs:

  1. builder.Services.AddMsalAuthentication(options =>
  2. {
  3. var authentication = options.ProviderOptions.Authentication;
  4. authentication.Authority =
  5. authentication.ClientId = "{CLIENT ID}";
  6. authentication.ValidateAuthority = false;
  7. options.ProviderOptions.DefaultAccessTokenScopes.Add(
  8. "{APP ID URI}/{DEFAULT SCOPE}");
  9. });

Blazor WebAssembly 模板自动配置应用程序,以便为提供给 dotnet new 命令({APP ID URI}/{DEFAULT SCOPE})的默认作用域的安全 API 请求访问令牌。

默认访问令牌范围表示访问令牌作用域的列表:

  • 默认情况下,在登录请求中包括。
  • 用于在身份验证后立即设置访问令牌。

对于每个 Azure Active Directory 规则,所有作用域都必须属于同一应用。可以根据需要为其他 API 应用添加其他作用域:

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

索引页面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 组件管理将未经授权的用户重定向到登录页。

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. <button class="nav-link btn btn-link" @onclick="BeginSignOut">
  8. Log out
  9. </button>
  10. </Authorized>
  11. <NotAuthorized>
  12. <a href="authentication/login">Log in</a>
  13. </NotAuthorized>
  14. </AuthorizeView>
  15. private async Task BeginSignOut(MouseEventArgs args)
  16. {
  17. await SignOutManager.SetSignOutState();
  18. Navigation.NavigateTo("authentication/logout");
  19. }
  20. }

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. }

FetchData 组件FetchData component

FetchData 组件显示了如何:

  • 设置访问令牌。
  • 使用访问令牌调用服务器应用中受保护的资源 API。

@attribute [Authorize] 指令向 Blazor WebAssembly 授权系统表明,用户必须获得授权才能访问此组件。如果客户端应用程序中存在该属性,则不会阻止在没有正确凭据的情况下调用服务器上的 API。服务器应用程序还必须在适当的终结点上使用 [Authorize],才能正确地对其进行保护。

AuthenticationService.RequestAccessToken(); 负责请求可添加到请求中的访问令牌,以调用 API。如果该令牌已缓存,或者该服务在没有用户交互的情况下能够预配新的访问令牌,则令牌请求会成功。否则,令牌请求会失败。

为了获得要包含在请求中的实际标记,应用程序必须通过调用 tokenResult.TryGetToken(out var token)来检查请求是否成功。

如果请求成功,将使用访问令牌填充令牌变量。此标记的 Value 属性公开要包含在 Authorization 请求标头中的文本字符串。

如果请求失败,因为无法在没有用户交互的情况下进行设置,令牌结果将包含重定向 URL。导航到此 URL 后,用户将进入登录页,并在身份验证成功后返回到当前页面。

  1. @page "/fetchdata"
  2. ...
  3. @attribute [Authorize]
  4. ...
  5. @code {
  6. private WeatherForecast[] forecasts;
  7. protected override async Task OnInitializedAsync()
  8. {
  9. var httpClient = new HttpClient();
  10. httpClient.BaseAddress = new Uri(Navigation.BaseUri);
  11. var tokenResult = await AuthenticationService.RequestAccessToken();
  12. if (tokenResult.TryGetToken(out var token))
  13. {
  14. httpClient.DefaultRequestHeaders.Add("Authorization",
  15. $"Bearer {token.Value}");
  16. forecasts = await httpClient.GetJsonAsync<WeatherForecast[]>(
  17. "WeatherForecast");
  18. }
  19. else
  20. {
  21. Navigation.NavigateTo(tokenResult.RedirectUrl);
  22. }
  23. }

有关详细信息,请参阅在执行身份验证操作之前保存应用程序状态

运行应用Run the app

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

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

其他资源Additional resources