TL;DR本教程介绍了Blazor框架,指导你用C#构建一个简单的Web应用程序。它还将向你展示如何将你的Blazor应用程序与Auth0集成,以确保其安全性。你可以在这个GitHub资源库中找到完整的应用程序代码。
什么是Blazor?
Blazor越来越受欢迎,特别是在.NET Core 3.0发布后,它以许多有趣的功能丰富了它。随后的.NET版本巩固了它的基础,人们对它的兴趣越来越大,以至于微软在它的未来上下了很大的赌注。但Blazor究竟是什么?
Blazor是一个用.NET构建客户端网络应用的编程框架。它允许.NET开发人员使用他们的C#和Razor知识来构建在浏览器中运行的交互式用户界面。用Blazor开发客户端应用程序给.NET开发人员带来了一些好处。
- 他们使用C#和Razor,而不是JavaScript和HTML。
- 他们可以利用整个.NET的功能。
- 他们可以在服务器和客户端共享代码。
- 他们可以使用他们所习惯的.NET开发工具。
简而言之,Blazor向.NET开发人员承诺,让他们用自己熟悉的开发平台建立客户端Web应用程序。
托管模式
Blazor为您提供了两种方式来运行您的Web客户端应用程序。Blazor服务器和Blazor WebAssembly。这些被称为托管模式。
Blazor服务器托管模式在服务器上的ASP.NET Core应用程序中运行你的应用程序。UI被发送到浏览器,但UI更新和事件处理是在服务器端进行的。这类似于传统的Web应用程序,但客户端和服务器端之间的通信是通过SignalR连接进行的。下图让你了解了Blazor服务器托管模式的整体架构。
Blazor服务器托管模式有一些好处,比如客户端应用程序的下载量较小,而且与最近的浏览器兼容。然而,与传统的单页应用程序(SPA)相比,它也有一些缺点,比如由于大多数用户互动需要在客户端和服务器之间进行往返,因此延迟较高,而且在高流量情况下的可扩展性也存在挑战。
Blazor WebAssembly托管模式,也被称为Blazor WASM,让你的应用程序完全在用户的浏览器上运行。应用程序的全部代码,包括其依赖性和.NET运行时,被编译成WebAssembly,由用户的浏览器下载,并在本地执行。下图描述了Blazor WebAssembly的托管模式。
Blazor WebAssembly托管模式所提供的好处与单页应用所提供的好处相似。在下载之后,除了需要的交互之外,应用程序是独立于服务器的。另外,你不需要ASP.NET Core Web服务器来托管你的应用程序。你可以使用任何Web服务器,因为WebAssembly编译的结果只是一组静态文件。
在另一方面,你应该注意到这种托管模式的缺点。Blazor的WebAssembly托管模式要求浏览器支持WebAssembly。此外,应用程序的初始下载可能需要一些时间。
[ "你不需要ASP.NET Core Web服务器来托管Blazor WebAssembly应用程序。"(twitter.com/intent/twee…
Blazor路线图
Blazor承诺为.NET开发者提供一个巨大的机会。微软在Blazor项目上的目标非常宏大,尤其是对Blazor WebAssembly。在他们的设想中,Blazor WebAssembly不仅将成为主要的托管模式,而且还将推动客户端开发的巨大革命。
Blazor WebAssembly托管模式将包括编入WebAssembly的单页应用程序、渐进式Web应用程序、混合移动应用程序、基于电子的桌面应用程序和本地应用程序。
前提条件
在开始构建你的Blazor应用程序之前,你需要确保在你的机器上安装了正确的工具。特别是,您需要.NET 6.0 SDK或以上版本。您可以在终端窗口中输入以下命令,检查您是否安装了正确的版本。
dotnet --version
你应该得到的值是 6.0.100
或以上作为结果。如果你没有,你应该下载.NET SDK并将其安装在你的机器上。
如果你要使用Visual Studio,请注意,你至少需要使用Visual Studio 2019 16.8或Visual Studio for Mac 8.9。
注意:如果你将Visual Studio更新到最新版本,你会得到所需的.NET SDK捆绑。
构建一个Blazor服务器应用程序
要开始使用Blazor,您将建立一个简单的测验应用程序,显示一个有多个答案的问题列表,并根据您提供的正确答案给您打分。你将使用Blazor服务器托管模式创建这个应用程序。如果你对使用Blazor WebAssembly来构建和保护同样的应用程序感兴趣,请查看这篇文章。
因此,在终端窗口中输入以下命令,创建一个基本的Blazor服务器项目。
dotnet new blazorserver -o QuizManager
该命令使用blazorserver
模板,在QuizManager
文件夹中为你的应用程序生成项目。这个新创建的文件夹有很多内容,但除了根文件夹外,你要接触的相关文件夹是。
Data
文件夹:它包含模型和实现业务逻辑的服务。Pages
文件夹:它包含生成HTML视图的Razor组件。特别是,这个文件夹包含了_Host.cshtml
Razor页面,它作为Web UI的起点。Shared
文件夹:它包含Razor组件和其他页面间共享的元素。
创建模型和服务
作为第一步,删除Data
文件夹内的文件。接下来,在这个文件夹中添加一个 QuizItem.cs
文件,并粘贴以下代码。
// Data/QuizItem.cs
namespace QuizManager.Data
{
public class QuizItem
{
public string Question { get; set; }
public List<string> Choices { get; set; }
public int AnswerIndex { get; set; }
public int Score { get; set; }
public QuizItem()
{
Choices = new List<string>();
}
}
}
这个类为测验的每个项目实现了模型。它提供了一个问题,一个可能的答案列表,正确答案的零基指数,以及用户给出正确答案时分配的分数。
在同一个Data
文件夹中,添加第二个文件,名为 QuizService.cs
的文件,内容如下。
// Data/QuizService.cs
namespace QuizManager.Data
{
public class QuizService
{
private static readonly List<QuizItem> Quiz;
static QuizService()
{
Quiz = new List<QuizItem> {
new QuizItem
{
Question = "Which of the following is the name of a Leonardo da Vinci's masterpiece?",
Choices = new List<string> {"Sunflowers", "Mona Lisa", "The Kiss"},
AnswerIndex = 1,
Score = 3
},
new QuizItem
{
Question = "Which of the following novels was written by Miguel de Cervantes?",
Choices = new List<string> {"The Ingenious Gentleman Don Quixote of La Mancia", "The Life of Gargantua and of Pantagruel", "One Hundred Years of Solitude"},
AnswerIndex = 0,
Score = 5
}
};
}
public Task<List<QuizItem>> GetQuizAsync()
{
return Task.FromResult(Quiz);
}
}
}
该类将测验定义为由构造函数初始化的QuizItem
实例的列表。 QuizService()
构造函数初始化的列表。为了简单起见,该列表用静态变量实现,但在真实世界的情况下,它应该被持久化到数据库中。该 GetQuizAsync()
方法只是返回Quiz
变量的值。
现在,移动到你项目的根目录,编辑 Program.cs
文件,应用下面代码中的修改。
// Program.cs
using Microsoft.AspNetCore.Components;
// ...other using clauses...
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
// 👇 existing code
//builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddSingleton<QuizService>();
// ☝️ new code
var app = builder.Build();
// ...existing code...
通过这一改变,你注册了你上面定义的QuizService
服务,而不是默认Blazor项目模板中的示例应用程序的服务。
创建Razor组件
现在你已经创建了应用程序的模型和服务,现在是实现用户界面的时候了。Blazor利用Razor作为模板处理器来生成动态HTML。特别是,Blazor使用Razor组件来建立应用程序的用户界面。Razor组件是独立的标记和代码单元,即使在其他项目中也可以嵌套和重复使用。它们在文件中的实现是以 .razor
后缀的文件中实现。
Tweet This(twitter.com/intent/twee…)
为了向用户展示测验并让他们与之互动,你需要将一个特定的视图作为Razor组件来实现。因此,移动到Pages
文件夹中,将 Counter.razor
和 FetchData.razor
文件。这些文件属于默认的样本项目。然后,在同一文件夹中添加 QuizViewer.razor
文件,其内容如下。
// Pages/QuizViewer.razor
@page "/quizViewer"
@using QuizManager.Data
@inject QuizService QuizRepository
<h1>Take your quiz!</h1>
<p>Your current score is @currentScore</p>
@if (quiz == null)
{
<p><em>Loading...</em></p>
}
else
{
int quizIndex = 0;
@foreach (var quizItem in quiz)
{
<section>
<h3>@quizItem.Question</h3>
<div class="form-check">
@{
int choiceIndex = 0;
quizScores.Add(0);
}
@foreach (var choice in quizItem.Choices)
{
int currentQuizIndex = quizIndex;
<input class="form-check-input" type="radio" name="@quizIndex" value="@choiceIndex" @onchange="@((eventArgs) => UpdateScore(Convert.ToInt32(eventArgs.Value), currentQuizIndex))"/>@choice<br>
choiceIndex++;
}
</div>
</section>
quizIndex++;
}
}
@code {
List<QuizItem> quiz;
List<int> quizScores = new List<int>();
int currentScore = 0;
protected override async Task OnInitializedAsync()
{
quiz = await QuizRepository.GetQuizAsync();
}
void UpdateScore(int chosenAnswerIndex, int quizIndex)
{
var quizItem = quiz[quizIndex];
if (chosenAnswerIndex == quizItem.AnswerIndex)
{
quizScores[quizIndex] = quizItem.Score;
} else
{
quizScores[quizIndex] = 0;
}
currentScore = quizScores.Sum();
}
}
看一下这个组件的代码。它的第一行使用@page
指令将这个组件定义为一个页面,这是一个UI元素,可以通过Blazor的路由系统中的一个地址(在本例中)直接到达。/quizViewer
在本例中)直接到达的UI元素。然后,你有一个@using
指令,它提供了对你定义 的命名空间的访问。 QuizManager.Data
命名空间,在那里你定义了QuizItem
模型和QuizService
服务。@inject
指令要求依赖性注入系统获得一个映射到QuizRepository
变量的QuizService
类的实例。
在这些初始化之后,你会发现定义了用户界面的标记。正如你所看到的,这部分是HTML和C#代码的混合体,其目的是建立问题列表,并以单选按钮表示各自可能的答案。
该组件的最后一个块被包含在@code
指令中。这是你放置组件的逻辑的地方。在QuizViewer
组件的例子中,你有 OnInitializedAsync()
和 UpdateScore()
方法。第一个方法在组件被初始化时被调用,它基本上是通过调用 GetQuizAsync()``QuizRepository
的方法来获取测验数据。第二个方法 UpdateScore()
当用户点击其中一个建议的答案时,该方法被调用,并根据用户选择的答案更新分配的分数列表。在同一方法中,当前分数的值被计算出来并分配给currentScore
。这个变量的值显示在问题列表的上方,你可以在标记中看到。
现在,去应用最后的修饰,移动到Shared
文件夹中,用以下代码替换掉 NavMenu.razor
文件的内容,替换为以下代码。
// Shared/NavMenu.razor
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">QuizManager</a>
<button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="quizViewer">
<span class="oi oi-list-rich" aria-hidden="true"></span> Quiz
</NavLink>
</div>
</nav>
</div>
@code {
private bool collapseNavMenu = true;
private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}
该 NavMenu.razor
文件包含了应用程序的导航栏组件的定义。你放其这个文件的代码定义了一个由两个项目组成的导航菜单:一个指向主页,另一个指向QuizViewer
组件。
你不打算改变项目根目录下的App组件所实现的 App.razor
文件实现的,但还是值得一看。
// App.razor
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
这个组件通过使用内置的Router组件将Blazor路由系统连接到你的应用程序。它可以在你的应用程序的页面中进行导航,区分找到的页面和不存在的页面。关于Blazor路由系统的更多信息,请查看官方文档。
⚠️如果你使用的是Mac,请阅读!⚠️
在撰写本文时,Mac用户在通过.NET CLI运行ASP.NET Core应用程序时受到了一个问题的影响。在运行.NET CLI时,你可能会得到以下对话窗口。
这是由于MacOS上的.NET CLI的一个已知问题。目前的解决方法是,你打开 QuizManager.csproj
文件并添加 <UseAppHost>false</UseAppHost>
元素,如下图所示。
<!-- QuizManager.csproj -->
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<!--👇 new element --->
<UseAppHost>false</UseAppHost>
</PropertyGroup>
</Project>
如果你使用Visual Studio,你不会受到这个问题的影响。
运行你的Blazor服务器应用程序
现在你有了你的测验应用程序,所以在终端窗口中输入以下命令来启动它。
dotnet run
如果这是你第一次运行ASP.NET Core应用程序,你应该信任.NET Core SDK中包含的HTTPS开发证书。这项任务取决于你的操作系统。请看一下官方文件,以应用正确的程序。
你也可能被要求允许应用程序访问开发者证书密钥。
几秒钟后,你应该让你的应用程序启动和运行。看一下你的终端窗口,得到你的应用程序正在监听的地址。在我的例子中,我得到的地址是 https://localhost:7290
,我将在整个文章中提到它。
从.NET 6.0开始,任何通过模板创建的ASP.NET项目都会为HTTP分配一个5000至5300之间的随机端口,为HTTPS分配7000至7300之间的随机端口。更多信息请参见本文档。
因此,如果你用这个地址打开浏览器,你应该可以进入主页,如下图所示。
选择导航菜单中的 "测验"项目,你应该得到你到目前为止建立的互动测验。它看起来应该如下图所示。
如果你打开浏览器的开发工具,点击网络标签,然后刷新,你会发现应用程序的客户端和服务器端之间的通信并没有使用HTTP,而是由SignalR管理的双向二进制通信。下图显示了Chrome开发工具中的WebSocket通道。
用Auth0保证应用程序的安全
现在你已经有了一个作为Blazor服务器应用程序实现的工作测验Web应用程序。为了确保该应用的安全,您将学习如何将其与Auth0服务集成。
创建Auth0应用程序
确保Blazor服务器应用程序安全的第一步是访问Auth0仪表板,注册你的Auth0应用程序。如果你没有Auth0账户,你可以现在就注册一个免费账户。
免费试用最强大的认证平台。开始吧→
进入仪表板后,移动到应用程序部分,并遵循以下步骤。
- 单击 "创建应用程序"。
- 为您的应用程序提供一个友好的名称(例如,Quiz Blazor服务器应用程序),并选择常规Web应用程序作为应用程序类型。
- 最后,单击 "创建"按钮。
这些步骤使Auth0知道你的Blazor应用程序,并将允许你控制访问。
在应用程序被注册后,移动到 "设置"选项卡,注意你的Auth0域和客户ID。然后,在同一表格中,将该值分配给 https://localhost:<YOUR_PORT_NUMBER>/callback
到允许的回调URLs字段,以及 https://localhost:<YOUR_PORT_NUMBER>/
到允许注销的URLs字段。将 <YOUR_PORT_NUMBER>
占位符替换为分配给你的应用程序的实际端口号。在我的例子中,这些值是 https://localhost:7290/callback
和 https://localhost:7290/
.
第一个值告诉Auth0在用户认证后要回调哪个URL。第二个值告诉Auth0,用户在注销后应被重定向到哪个URL。
点击 "保存更改"按钮来应用它们。
配置你的Blazor应用程序
打开Blazor服务器项目根目录下的 appsettings.json
文件,并将其内容替换为以下内容。
// appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Auth0": {
"Domain": "YOUR_AUTH0_DOMAIN",
"ClientId": "YOUR_CLIENT_ID"
}
}
替换占位符 YOUR_AUTH0_DOMAIN
和 YOUR_CLIENT_ID
替换为从Auth0仪表板上获取的相应数值。
与Auth0集成
现在,在终端窗口中运行以下命令,安装Auth0 ASP.NET Core Authentication SDK。
dotnet add package Auth0.AspNetCore.Authentication
Auth0 ASP.NET Core SDK可以让您轻松地将基于OpenID Connect的认证整合到您的应用程序中,而无需处理所有低级别的细节。
如果你想了解更多,这篇博文为你提供了关于Auth0 ASP.NET Core SDK的概述。
安装完成后,打开 Program.cs
文件,并修改其内容如下。
// Program.cs
using QuizManager.Data;
using Auth0.AspNetCore.Authentication; // 👈 new code
var builder = WebApplication.CreateBuilder(args);
// 👇 new code
builder.Services
.AddAuth0WebAppAuthentication(options => {
options.Domain = builder.Configuration["Auth0:Domain"];
options.ClientId = builder.Configuration["Auth0:ClientId"];
});
// 👆 new code
// Add services to the container.
builder.Services.AddRazorPages();
// ...existing code...
app.UseRouting();
app.UseAuthentication(); // 👈 new code
app.UseAuthorization(); // 👈 new code
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
在突出显示的代码之后,你在文件的开头添加了一个对 Auth0.AspNetCore.Authentication
命名空间的引用,在文件的开头。然后你调用了 AddAuth0WebAppAuthentication()
方法,并将Auth0域和客户端ID作为参数。这些Auth0配置参数取自你之前准备的 appsetting.json
配置文件。最后,你调用了 UseAuthentication()
和 UseAuthorization()
方法来启用认证和授权中间件。
现在你的应用程序已经有了支持通过Auth0认证的基础设施。
保证服务器端的安全
为了防止未经授权的用户访问你的应用程序的服务器端功能,你需要保护它们。因此,打开 Index.razor``Pages
文件,添加Authorize
属性,如下图所示。
// Pages/Index.razor
@page "/"
@attribute [Authorize]
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />
将同样的属性添加到 QuizViewer.razor
组件也添加同样的属性。
// Pages/QuizViewer.razor
@page "/quizViewer"
@attribute [Authorize]
@using QuizManager.Data
@inject QuizService QuizRepository
// ...existing code...
这可以确保你的页面的服务器端渲染只由授权用户触发。
创建登录和注销端点
如前所述,在Blazor服务器托管模式中,客户端和服务器端之间的通信不是通过HTTP进行的,而是通过SignalR。由于Auth0使用了像OpenID和OAuth这样依赖HTTP的标准协议,所以你需要提供一种方法,将这些协议带到Blazor上。
为了解决这个问题,你要创建两个端点。 /login
和 /logout
,将登录和注销的请求重定向到Auth0。两个标准的Razor页面在这些端点后面做出响应。
因此,通过在终端窗口中输入以下命令,将Loginrazor页面添加到项目中。
dotnet new page --name Login --namespace QuizManager.Pages --output Pages
这个命令在Pages
文件夹中创建了两个文件。 Login.cshtml
和 Login.cshtml.cs
.
打开 Login.cshtml.cs
文件夹中的Pages
,并将其内容替换为以下内容。
// Pages/Login.cshtml.cs
using Microsoft.AspNetCore.Authentication;
using Auth0.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace QuizManager.Pages
{
public class LoginModel : PageModel
{
public async Task OnGet(string redirectUri)
{
var authenticationProperties = new LoginAuthenticationPropertiesBuilder()
.WithRedirectUri(redirectUri)
.Build();
await HttpContext.ChallengeAsync(Auth0Constants.AuthenticationScheme, authenticationProperties);
}
}
}
这段代码创建了一套登录所需的认证属性,并通过Auth0触发了认证过程。
然后,通过输入以下命令来添加注销页面。
dotnet new page --name Logout --namespace QuizManager.Pages --output Pages
与前面的情况类似,你将在Pages
文件夹中得到两个新文件。 Logout.cshtml
和 Logout.cshtml.cs
.
用下面的代码替换 Logout.cshtml.cs
文件的内容替换为以下代码。
// Pages/Logout.cshtml.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Authentication;
using Auth0.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
namespace QuizManager.Pages
{
public class LogoutModel : PageModel
{
[Authorize]
public async Task OnGet()
{
var authenticationProperties = new LogoutAuthenticationPropertiesBuilder()
.WithRedirectUri("/")
.Build();
await HttpContext.SignOutAsync(Auth0Constants.AuthenticationScheme, authenticationProperties);
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
}
}
这段代码将关闭用户在Blazor应用程序和Auth0上的会话。
保护客户端的安全
现在,你必须保护Blazor应用程序的客户端,以便用户在登录或未登录时看到不同的内容。
打开项目根目录下的 App.razor
文件,并用下面的标记替换其内容。
// App.razor
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<Authorizing>
<p>Determining session state, please wait...</p>
</Authorizing>
<NotAuthorized>
<h1>Sorry</h1>
<p>You're not authorized to reach this page. You need to log in.</p>
</NotAuthorized>
</AuthorizeRouteView>
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
在这里,你使用的是AuthorizeRouteView
,只有当用户被授权时,才会显示相关的组件。在实践中,MainLayout
组件的内容将只显示给授权用户。如果用户没有被授权,他们将看到由NotAuthorized
组件包裹的内容。如果授权正在进行中,用户将看到Authorizing
组件内的内容。CascadingAuthenticationState
组件将把当前的认证状态传播给内部组件,以便它们能够一致地工作。
[ "AuthorizeRouteView组件允许你控制对Blazor应用程序的UI部分的访问。"](twitter.com/intent/twee…
在这一点上,确保你在QuizManager
文件夹中,并在终端运行dotnet run
。当用户试图访问你的应用程序时,他们将只看到未授权的信息,如下图所示。
所以,你需要一种方法让用户进行认证。为此创建一个Razor组件,在 文件夹中添加一个 AccessControl.razor
文件,内容如下:Shared
。
// Shared/AccessControl.razor
<AuthorizeView>
<Authorized>
<a href="logout">Log out</a>
</Authorized>
<NotAuthorized>
<a href="login?redirectUri=/">Log in</a>
</NotAuthorized>
</AuthorizeView>
这个组件使用Authorized
组件让授权用户看到注销链接,使用NotAuthorized
组件让未授权用户访问登录链接。这两个链接都指向你之前创建的端点。特别是,登录链接将主页指定为用户认证后重定向的URI。
最后一步是把这个组件放在你的Blazor应用程序的顶部栏中。因此,将该文件的内容替换为 MainLayout.razor
文件的内容,改为以下内容。
// Shared/MainLayout.razor
@inherits LayoutComponentBase
<PageTitle>QuizManager</PageTitle>
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<AccessControl /> //👈 new markup
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>
<article class="content px-4">
@Body
</article>
</main>
</div>
正如你所看到的,唯一的区别是在 "*关于 "*链接之前增加了AccessControl
组件。
现在,你的Blazor应用程序只能被授权用户访问。当用户点击登录链接时,他们将被重定向到Auth0通用登录页面进行认证。认证完成后,他们将回到你的应用程序的主页,并能够参加测验。
访问用户资料
一旦你在你的Blazor服务器应用程序中添加了认证,你可能需要访问一些关于认证用户的信息,例如他们的名字和照片。默认情况下,Auth0 ASP.NET Core认证SDK会在认证过程中为您获取这些信息。
为了在页面上显示用户的名字和图片,请将该组件的内容改为 Index.razor
组件的内容如下。
// Pages/Index.razor
@page "/"
@inject AuthenticationStateProvider AuthState
@attribute [Authorize]
<PageTitle>Quiz Manager</PageTitle>
<h1>Welcome, @Username!</h1>
You can only see this content if you're authenticated.
<br />
<img src="@Picture">
@code {
private string Username = "Anonymous User";
private string Picture = "";
protected override async Task OnInitializedAsync()
{
var state = await AuthState.GetAuthenticationStateAsync();
Username = state.User.Identity.Name?? string.Empty;
Picture = state.User.Claims
.Where(c => c.Type.Equals("picture"))
.Select(c => c.Value)
.FirstOrDefault() ?? string.Empty;
await base.OnInitializedAsync();
}
}
在这个新版本的组件中,你注入了AuthenticationStateProvider
,它为你提供了关于当前认证状态的信息。你在代码块中通过使用其 GetAuthenticationStateAsync()
方法获得实际的认证状态。由于认证状态,你可以提取当前用户的名字和照片,并将它们分配给Username
和Picture
变量。这些是你在组件的标记中用来定制这个视图的变量。
在这一点上,你可以运行你的应用程序,登录,并得到一个类似于下面的主页。
总结
本文介绍了Blazor的基本知识,这个编程框架允许您使用C#和.NET平台来构建Web客户端应用程序。你了解了两种托管模式,即Blazor服务器和Blazor WebAssembly,并使用Blazor服务器托管模式建立了一个测验管理器应用程序。
最后,你通过将Blazor Server应用程序与Auth0集成,确保了它的安全性。
文章中所构建的应用程序的完整源代码可以从这个GitHub仓库下载。