- 使用 Azure Active Directory B2C 保护 ASP.NET Core Blazor WebAssembly 托管应用Secure an ASP.NET Core Blazor WebAssembly hosted app with Azure Active Directory B2C
- 在 AAD B2C 中注册应用并创建解决方案Register apps in AAD B2C and create solution
- 服务器应用配置Server app configuration
- 客户端应用配置Client app configuration
- 运行应用Run the app
- 疑难解答Troubleshoot
- 其他资源Additional resources
使用 Azure Active Directory B2C 保护 ASP.NET Core Blazor WebAssembly 托管应用Secure an ASP.NET Core Blazor WebAssembly hosted app with Azure Active Directory B2C
本文内容
作者: 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) B2C进行身份验证的 Blazor WebAssembly 独立应用程序。
在 AAD B2C 中注册应用并创建解决方案Register apps in AAD B2C and create solution
创建租户Create a tenant
按照教程:创建 Azure Active Directory B2C 租户中的指导创建 AAD B2C 租户并记录以下信息:
- AAD B2C 实例(例如,
https://contoso.b2clogin.com/
,其中包含尾随斜杠) - AAD B2C 租户域(例如,
contoso.onmicrosoft.com
)
注册服务器 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 (例如
https://contoso.onmicrosoft.com/11111111-1111-1111-1111-111111111111
,Azure 门户可能会将值默认为客户端 ID) - 默认作用域(例如
API.Access
)
注册客户端应用Register a client app
请按照教程:将应用程序注册到 Azure Active Directory B2C中的指导,在 Azure 门户的Azure Active Directory > 应用注册区域中注册客户端应用的 AAD 应用:
- 选择“新注册”。
- 提供应用的名称(例如, Blazor 客户端 AAD B2C)。
- 对于支持的帐户类型,请选择任何组织目录或任何标识提供者中的帐户。用于对具有 Azure AD B2C 的用户进行身份验证。(多租户)。
- 将 "重定向 uri " 下拉集保持设置为 " Web",并提供
https://localhost:5001/authentication/login-callback
的重定向 uri。 - 确认权限 > 向 openid 授予 "管理员以免" 和 "offline_access" 权限已启用。
选择“注册”。
在 "身份验证" > 平台配置 > Web:确认存在
https://localhost:5001/authentication/login-callback
的重定向 URI 。- 对于 "隐式授予",选中 "访问令牌" 和 " ID 令牌" 对应的复选框。
- 此体验可接受应用的其余默认值。
选择“保存”按钮。
在 " API 权限:确认应用程序已Microsoft Graph > 用户。读取权限。
- 选择 "添加权限",然后选择 "我的 api" 。
- 从 "名称" 列中选择 "服务器 API 应用" (例如, Blazor server AAD B2C")。
- 打开API列表。
- 启用对 API 的访问(例如
API.Access
)。 - 选择“添加权限”。
- 选择 "为 {租户名称} 授予管理内容" 按钮。请选择“是”以确认。
在Home > Azure AD B2C > 用户流:
至少,选择 "应用程序声明" > "显示名称用户属性",以填充 LoginDisplay
组件中的 context.User.Identity.Name
(Shared/LoginDisplay)。
记录以下信息:
- 记录客户端应用应用程序 Id (客户端 id)(例如
33333333-3333-3333-3333-333333333333
)。 - 记录为应用创建的注册和登录用户流名称(例如
B2C_1_signupsignin
)。
创建应用Create the app
将以下命令中的占位符替换为前面记录的信息,然后在命令行界面中执行命令:
dotnet new blazorwasm -au IndividualB2C --aad-b2c-instance "{AAD B2C INSTANCE}" --api-client-id "{SERVER API APP CLIENT ID}" --app-id-uri "{APP ID URI}" --client-id "{CLIENT APP CLIENT ID}" --default-scope "{DEFAULT SCOPE}" --domain "{DOMAIN}" -ho -ssp "{SIGN UP OR SIGN IN POLICY}" --tenant-id "{TENANT ID}"
若要指定输出位置(如果它不存在,则创建一个项目文件夹),请在命令中包含 output 选项,其中包含一个路径(例如 -o BlazorSample
)。文件夹名称还会成为项目名称的一部分。
服务器应用配置Server app configuration
本部分适用于解决方案的服务器应用。
身份验证包Authentication package
Microsoft.AspNetCore.Authentication.AzureAD.UI
提供对 ASP.NET Core Web Api 的身份验证和授权调用的支持:
<PackageReference Include="Microsoft.AspNetCore.Authentication.AzureAD.UI"
Version="3.1.0" />
身份验证服务支持Authentication service support
AddAuthentication
方法在应用中设置身份验证服务,并将 JWT 持有者处理程序配置为默认身份验证方法。AddAzureADBearer
方法在验证 Azure Active Directory 发出的令牌所需的 JWT 持有者处理程序中设置特定参数:
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
UseAuthentication
和 UseAuthorization
确保:
- 应用尝试分析和验证传入请求的令牌。
- 任何试图访问受保护资源的请求均不正确。
app.UseAuthentication();
app.UseAuthorization();
应用设置App settings
Appsettings文件包含用于配置用于验证访问令牌的 JWT 持有者处理程序的选项。
{
"AzureAd": {
"Instance": "https://{ORGANIZATION}.b2clogin.com/",
"ClientId": "{API CLIENT ID}",
"Domain": "{DOMAIN}",
"SignUpSignInPolicyId": "{SIGN UP OR SIGN IN POLICY}"
}
}
WeatherForecast 控制器WeatherForecast controller
WeatherForecast 控制器(控制器/WeatherForecastController)公开受保护的 API,并将 [Authorize]
特性应用到控制器。务必要了解:
- 此 API 控制器中的
[Authorize]
属性是保护此 API 不受未经授权的访问的唯一操作。 - Blazor WebAssembly 应用程序中使用的
[Authorize]
属性仅作为对应用程序的提示,用户应授权该应用程序正常工作。
[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
...
}
}
客户端应用配置Client app configuration
本部分适用于解决方案的客户端应用。
身份验证包Authentication package
创建应用以使用单个 B2C 帐户(IndividualB2C
)时,应用会自动接收Microsoft 身份验证库(Microsoft.Authentication.WebAssembly.Msal
)的包引用。包提供一组基元,可帮助应用对用户进行身份验证,并获取令牌以调用受保护的 Api。
如果向应用程序中添加身份验证,请将包手动添加到应用的项目文件中:
<PackageReference Include="Microsoft.Authentication.WebAssembly.Msal"
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:
builder.Services.AddMsalAuthentication(options =>
{
var authentication = options.ProviderOptions.Authentication;
authentication.Authority =
"{AAD B2C INSTANCE}{DOMAIN}/{SIGN UP OR SIGN IN POLICY}";
authentication.ClientId = "{CLIENT ID}";
authentication.ValidateAuthority = false;
options.ProviderOptions.DefaultAccessTokenScopes.Add(
"{APP ID URI}/{DEFAULT SCOPE}");
});
AddMsalAuthentication
方法接受回调,以配置对应用进行身份验证所需的参数。注册应用时,可以从 Azure 门户 AAD 配置获取配置应用所需的值。
Blazor WebAssembly 模板自动配置应用程序,以便为提供给 dotnet new
命令({APP ID URI}/{DEFAULT SCOPE}
)的默认作用域的安全 API 请求访问令牌。
默认访问令牌范围表示访问令牌作用域的列表:
- 默认情况下,在登录请求中包括。
- 用于在身份验证后立即设置访问令牌。
对于每个 Azure Active Directory 规则,所有作用域都必须属于同一应用。可以根据需要为其他 API 应用添加其他作用域:
builder.Services.AddMsalAuthentication(options =>
{
...
options.ProviderOptions.DefaultAccessTokenScopes.Add(
"{APP ID URI}/{SCOPE}");
});
索引页面Index page
"索引页(wwwroot/index.html)" 页包含一个用于在 JavaScript 中定义 AuthenticationService
的脚本。AuthenticationService
处理 OIDC 协议的低级别详细信息。应用在内部调用在脚本中定义的方法来执行身份验证操作。
<script src="_content/Microsoft.Authentication.WebAssembly.Msal/
AuthenticationService.js"></script>
应用组件App component
App
组件(app.config)类似于在 Blazor Server apps 中找到 App
组件:
CascadingAuthenticationState
组件管理向应用程序的其余部分公开AuthenticationState
。AuthorizeRouteView
组件确保当前用户有权访问给定页面或呈现RedirectToLogin
组件。RedirectToLogin
组件管理将未经授权的用户重定向到登录页。
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData"
DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
<RedirectToLogin />
</NotAuthorized>
</AuthorizeRouteView>
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
RedirectToLogin 组件RedirectToLogin component
RedirectToLogin
组件(Shared/RedirectToLogin):
- 管理将未经授权的用户重定向到登录页。
- 保留用户尝试访问的当前 URL,以便在身份验证成功时可以将其返回到该页。
@inject NavigationManager Navigation
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@code {
protected override void OnInitialized()
{
Navigation.NavigateTo($"authentication/login?returnUrl={Navigation.Uri}");
}
}
LoginDisplay 组件LoginDisplay component
LoginDisplay
组件(shared/LoginDisplay)在 MainLayout
组件(shared/MainLayout)中呈现并管理以下行为:
- 对于经过身份验证的用户:
- 显示当前用户名。
- 提供用于注销应用的按钮。
- 对于匿名用户,提供登录选项。
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject NavigationManager Navigation
@inject SignOutSessionStateManager SignOutManager
<AuthorizeView>
<Authorized>
Hello, @context.User.Identity.Name!
<button class="nav-link btn btn-link" @onclick="BeginSignOut">
Log out
</button>
</Authorized>
<NotAuthorized>
<a href="authentication/login">Log in</a>
</NotAuthorized>
</AuthorizeView>
@code {
private async Task BeginSignOut(MouseEventArgs args)
{
await SignOutManager.SetSignOutState();
Navigation.NavigateTo("authentication/logout");
}
}
身份验证组件Authentication component
Authentication
组件(Pages/Authentication)生成的页面定义处理不同的身份验证阶段所需的路由。
RemoteAuthenticatorView
组件:
- 由
Microsoft.AspNetCore.Components.WebAssembly.Authentication
包提供。 - 管理在每个身份验证阶段执行适当的操作。
@page "/authentication/{action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
<RemoteAuthenticatorView Action="@Action" />
@code {
[Parameter]
public string Action { get; set; }
}
FetchData 组件FetchData component
FetchData
组件显示了如何:
- 设置访问令牌。
- 使用访问令牌调用服务器应用中受保护的资源 API。
@attribute [Authorize]
指令向 Blazor WebAssembly 授权系统表明,用户必须获得授权才能访问此组件。如果客户端应用程序中存在该属性,则不会阻止在没有正确凭据的情况下调用服务器上的 API。服务器应用程序还必须在适当的终结点上使用 [Authorize]
,才能正确地对其进行保护。
AuthenticationService.RequestAccessToken();
负责请求可添加到请求中的访问令牌,以调用 API。如果该令牌已缓存,或者该服务在没有用户交互的情况下能够预配新的访问令牌,则令牌请求会成功。否则,令牌请求会失败。
为了获得要包含在请求中的实际标记,应用程序必须通过调用 tokenResult.TryGetToken(out var token)
来检查请求是否成功。
如果请求成功,将使用访问令牌填充令牌变量。此标记的 Value
属性公开要包含在 Authorization
请求标头中的文本字符串。
如果请求失败,因为无法在没有用户交互的情况下进行设置,令牌结果将包含重定向 URL。导航到此 URL 后,用户将进入登录页,并在身份验证成功后返回到当前页面。
@page "/fetchdata"
...
@attribute [Authorize]
...
@code {
private WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(Navigation.BaseUri);
var tokenResult = await AuthenticationService.RequestAccessToken();
if (tokenResult.TryGetToken(out var token))
{
httpClient.DefaultRequestHeaders.Add("Authorization",
$"Bearer {token.Value}");
forecasts = await httpClient.GetJsonAsync<WeatherForecast[]>(
"WeatherForecast");
}
else
{
Navigation.NavigateTo(tokenResult.RedirectUrl);
}
}
}
有关详细信息,请参阅在执行身份验证操作之前保存应用程序状态。
运行应用Run the app
从服务器项目运行应用。使用 Visual Studio 时,请在解决方案资源管理器中选择服务器项目,并在工具栏中选择 "运行" 按钮,或从 "调试" 菜单启动应用程序。
疑难解答Troubleshoot
由于 ID 令牌和访问令牌可在登录尝试期间保持,因此,每次更新后,请使用浏览器的开发人员控制台清除浏览器 cookie:
- 应用的身份验证代码或配置设置。
- 应用的配置 OIDC 兼容的提供程序(例如 Azure Active Directory)。