作为.NET开发者,我们都听说最近Blazor比较火。Blazor是一个使用.NET构建交互式客户端Web UI的框架。我们将在本篇博客中重点介绍Blazor Server,它提供了在ASP.NET Core应用程序中在服务器托管Razor组件的支持。通过SignalR连接更新UI。本篇示例的授权认证的部分就利用Azure AD来做,并会从Microsoft Graph获取数据。
安装.NET Core 3.1 SDK,Visual Studio 2019或其他偏好的.NET IDE,还有就是Azure AD的租户,推荐使用免费的Microsoft 365开发者订阅。
打开Visual Studio 2019,如果它升级到了新版本,我们可以直接搜索Blazor去创建一个Blazor应用项目,并将Server的认证配置为Work or School Accounts。
通过配置这个选项,Visual Studio会在Azure AD中创建相应的应用程序注册,并对Blazor应用进行必要的配置以达到认证生效的拆箱即用状态,此时如果查看appsettings.json,可以看到类似下面的内容。
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "JFoxdave.onmicrosoft.com",
"TenantId": "" ,
"ClientId": "" ,
"CallbackPath": "/signin-oidc"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
同时在Azure AD的应用程序注册部分,我们也可以看到自动创建的应用程序注册。
不需要额外写任何代码,Blazor应用会在用户访问任何页面之前提示登录。我们可以点击F5直接在Visual Studio中运行应用进行简单的测试。在第一次访问网站时,我们还会被提示去批准应用所需的权限。
看起来我们今天的内容介绍完了,但实际上还有一些可以改进和扩展的地方。比如,默认模板使用的是V1版本的Azure AD终结点,而我们现在推荐使用Microsoft Identity平台,即V2版本的终结点。
首先,从NuGet安装Microsoft.Identity.Web和Microsoft.Identity.Web.UI这两个包,目前还是预览版,过不了多久就GA了。
然后,我们需要对代码进行一些更改,将旧的认证代码去掉,加入新的代码。
打开Startup.cs文件,将下面的代码
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
替换为
services.AddMicrosoftWebAppAuthentication(Configuration);
services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).AddMicrosoftIdentityUI();
最后,我们需要确保我们的应用使用的是V2版本的终结点进行登录登出操作的。打开LoginDisplay.razor,按照以下内容更新代码
<AuthorizeView>
<Authorized>
Hello, @context.User.Identity.Name!
<a href="MicrosoftIdentity/Account/SignOut">Log outa>
Authorized>
<NotAuthorized>
<a href="MicrosoftIdentity/Account/SignIn">Log ina>
NotAuthorized>
AuthorizeView>
大家应该注意到了,并没有专门的登录登出页面,因为它们被包含在Microsoft.Identity.Web这个dll中了。因此我们只需要将链接部分更新为“MicrosoftIdentity”,不需要其他更改了。
再次从Visual Studio启动我们的应用,如果我们的用户启用了更多的安全认证方式,那么就能够看到已经是V2版本的体验了。
如上面介绍的,我们只需要几行代码就可以使用Microsoft.Identity.Web库进行Azure AD的认证。
Microsoft Graph是什么就不再赘述了,之前讲过了好多遍。在这里我们想要应用能够读取用户的邮件信息,首先我们要在Azure AD中给应用程序注册授权 (Mail.Read) ,然后在我们应用的某个页面上写一些代码去读取并显示就可以了。
在Azure Portal的Azure AD页面找到我们的应用,添加权限,同时别忘了创建一个密钥,并把值拷贝下来留用。
然后将这些信息更新到我们项目的appsettings.json文件中
"ClientSecret": "4tL4m11h_zN-L.64l-9Pdm-4xGOq6uC~wb"
在startup.cs类中,我们需要更新代码以确保能够获取到具有权限的访问令牌,并将它保存到缓存中以便后续调用Microsoft Graph时使用。
services.AddMicrosoftWebAppAuthentication(Configuration)
.AddMicrosoftWebAppCallsWebApi(Configuration, new string[] { "User.Read", "Mail.Read" })
.AddInMemoryTokenCaches();
services.AddHttpClient();
接下来,我们需要更新FetchData.razor页面的代码来获取我们需要的信息。
@page "/fetchdata"
@inject IHttpClientFactory HttpClientFactory
@inject Microsoft.Identity.Web.ITokenAcquisition TokenAcquisitionService
<h1>Weather forecasth1>
<p>This component demonstrates fetching data from a service.p>
@if (messages == null)
{
<p><em>Loading...em>p>
}
else
{
<h1>Hello @userDisplayName !!!!h1>
<table class="table">
<thead>
<tr>
<th>Subjectth>
<th>Senderth>
<th>Received Timeth>
tr>
thead>
<tbody>
@foreach (var mail in messages)
{
<tr>
<td>@mail.Subjecttd>
<td>@mail.Sendertd>
<td>@mail.ReceivedTimetd>
tr>
}
tbody>
table>
}
@code {
private string userDisplayName;
private List<MailMessage> messages = new List<MailMessage>();
private HttpClient _httpClient;
protected override async Task OnInitializedAsync()
{
_httpClient = HttpClientFactory.CreateClient();
// get a token
var token = await TokenAcquisitionService.GetAccessTokenForUserAsync(new string[] { "User.Read", "Mail.Read" });
// make API call
_httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
var dataRequest = await _httpClient.GetAsync("https://graph.microsoft.com/beta/me");
if (dataRequest.IsSuccessStatusCode)
{
var userData = System.Text.Json.JsonDocument.Parse(await dataRequest.Content.ReadAsStreamAsync());
userDisplayName = userData.RootElement.GetProperty("displayName").GetString();
}
var mailRequest = await _httpClient.GetAsync("https://graph.microsoft.com/beta/me/messages?$select=subject,receivedDateTime,sender&$top=10");
if (mailRequest.IsSuccessStatusCode)
{
var mailData = System.Text.Json.JsonDocument.Parse(await mailRequest.Content.ReadAsStreamAsync());
var messagesArray = mailData.RootElement.GetProperty("value").EnumerateArray();
foreach (var m in messagesArray)
{
var message = new MailMessage();
message.Subject = m.GetProperty("subject").GetString();
message.Sender = m.GetProperty("sender").GetProperty("emailAddress").GetProperty("address").GetString();
message.ReceivedTime = m.GetProperty("receivedDateTime").GetDateTime();
messages.Add(message);
}
}
}
public class MailMessage
{
public string Subject;
public string Sender;
public DateTime ReceivedTime;
}
}
再次运行我们的应用,点击Fetch data页面,可以看到当前用户的邮件列表了。
这里如果出错就注销重新登录一下,要确保为应用更新的权限被批准。