如何使用 Azure Active Directory 认证和 Microsoft Graph 构建 Blazor Web 应用

英文原文:https://developer.microsoft.com/en-us/identity/blogs/how-to-build-a-blazor-web-app-with-azure-active-directory-authentication-and-microsoft-graph/

如果您是一个 .NET 开发者,你很可能听过过 Blazor 是一个最近的热门开发技术。Blazor 是一个使用 .NET Blazor 服务器来构建可交互客户端 Web 界面的框架。就是本文所专注的技术,提供了在 ASP.NET Core 应用中,在服务器端寄宿 Razor 组件的支持。UI 的更新通过 SignalR 连接进行。由于多数的应用都需要某些程度的验证和授权,这里将展示如何使用 Azure AD 实现验证的最佳方式,以及如何从 Microsoft Graph 获取数据。

先决条件

为了继续下面的演练,您需要最新版本的 NET Core 3.1 SDK, Visual Studio 2019 (可选,但是最好),并且拥有一个 Azure AD 的租户。如果你不能访问 Azure AD 租户,要么你可以免费注册一个 Microsoft 365 Developer program,或者创建一个免费的 Free Azure Trial 试用账号。

Blazor 与验证

如果你正在构建 Blazor (服务器端) 应用,那么我们有一些好消息。Visual Studio 和 CLI 模版支持开箱即用的验证支持。打开 Visual Studio,然后创建一个新的 Blazor 应用。我们将它命名为 "BlazorAppWithAuth",然后按照下面的动画进行。

通过选择工作或者学校账号验证选项,Visual Studio 将在 Azure AD 上创建适当的应用注册,并为 Blazor 应用配置开箱即用的验证所需的配置和代码。我们可以通过检查 appsettings.json 来确认。

并且,如果你到 Azure 门户的 Azure AD 注册页签中查看,还可以看到与 Visual Studio 中看到的信息想匹配的应用注册资料。

不需要写一行代码,我们的 Blazor 应用在用户访问任何页面之前,就可以提示用户。我们可以通过运行应用来快速测试该应用。在我们第一次访问该站点的时候,我们将被提示这些应用要求的 (默认) 权限,这里是读取用户的资料。

到了这一步,你可能认为我们的任务已经完成了,实际上,还有许多事情要做。首先,默认的模版使用了老式的,默认 v1 版本的 Azure AD 端点。如果你想知道为什么你应当使用 Microsoft identity platform 并使用 v2 版本的端点,你可以查看 Microsoft identity platform 文档

使用 Microsoft.Identity.Web 现代化验证

Build 2020 中,我们为 ASP.NET Core 3.1 (及后继版本) 宣布了一个新的验证和令牌管理库。新的库对复杂性进行了很棒的大量抽象,支持开发者只需要很少的代码就可以实现验证。而且,新库最大的优势是因为该库构建与 MSAL 之上,你不再需要使用两个单独的库来先进行验证,然后获得一个令牌来访问后端的 API。所以,让我们看一下如何迁移到这个最新的库上。

注意:Microsoft.Identity.Web 仍然在预览状态,它将很快发布

注意:现在 1.0 已经发布。发布时间:2020/10/1

注意:现在 1.1.0 已经发布,发布时间:Tuesday, October 6, 2020 (10/6/2020)

首先,我们需要下载这个新的 NuGet 包

  • Microsoft.Identity.Web - 1.1.0 已经发布
  • Microsoft.Identity.Web.UI - 1.1.0 已经发布

然后,我们需要修改几行代码来清除老的验证代码并切换到新的代码。

打开 Startup() 并替换代码,下面是原来的代码:

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();

使用 1.1 SDK 后,应该为:

services.AddMicrosoftIdentityWebAppAuthentication(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 out</a>
</Authorized>
<NotAuthorized>
<a href="MicrosoftIdentity/Account/SignIn">Log in</a>
</NotAuthorized>
</AuthorizeView>

你需要注意到,这里并没有特定的用来登录和登出的页面。相反,它们构建在 Microosft.Identity.Web.dll 内部。所以,在我们更新区域到 "MicrosoftIdentity" 的时候,不再需要其它的修改了。

当我们再次从 Visual Stuido 运行应用的时候,我们从 v2 中获得新的登录体验,例如无口令和多因子验证。最棒的是这些功能被设计为不需要修改任何代码,随着 Azure AD 的管理被配置为使用这些设置,所有的用户可以从更强的安全性中获益。

如你所见,使用很少的代码,我们可以借助于 Microsoft.Identity.Web 库基于 Azure AD 来验证用户。

从 Microsoft Graph 提取数据

Microsoft Graph 提供了大量的 API 来支持你基于用户自己的数据构建丰富的沉浸式的应用。在下面的步骤中,我们将拉取用户的电子邮件并将它们显示在应用内。为达到该目标,我们首先需要在 Azure AD 上扩展应用注册的权限,增加访问电子邮件数据的访问,然后我们需要在 Blazore 应用中添加一些代码来提取,并在其中的一个页面上显示数据。

Azure AD 门户

找到应用的注册,并进入 API 权限,选择添加新的权限,然后选择 Graph API,在这里,我们希望选择被代理权限,并选择 "Mail Read" 权限。

我们还需要创建一个用户密钥,因为我们的应用将需要一个验证令牌,和提取数据而不需要任何用户交互的方式。在同一个应用注册中,打开 Certificates 和 Secrets 页签,然后创建新的永不过期的密钥,如下所示:

确保你复制了密码,因为一旦你从该页面切换出去,你就再也不能访问它了。但是,如果需要的话,你总是可以重新创建它 - 这很简单且免费。

回到 Blazor 应用中,在 Visual Studio 中,我们首先需要在 appsettings.json 中添加客户密钥。在 AzureAD

的配置节,我们必须添加如下的行:

“ClientSecret”: “<your secret>”

在 Startup.cs 文件中,我们需要更新代码以确保使用正确的权限获取了适当的访问令牌,并将它保存在缓存中,以便我们在后继的应用中使用它访问 Microsoft Graph。我们将添加 HttpClient 到我们的服务管线中,来支持我们随后有效的发出 HTTP 调用到 Microsoft.Graph。

services.AddMicrosoftIdentityWebAppAuthentication(Configuration)
.EnableTokenAcquisitionToCallDownstreamApi( new string[] { "User.Read", "Mail.Read" })
.AddInMemoryTokenCaches(); services.AddHttpClient();

然后,我们需要更新在 FetchData.razor 页面中的代码来获取我们的电子邮件数据,来替代默认的天气数据。下面的代码包含了所有我们需要获取电子邮件并显示在页面上的代码。

@page "/fetchdata"

@inject IHttpClientFactory HttpClientFactory
@inject Microsoft.Identity.Web.ITokenAcquisition TokenAcquisitionService <h1>Weather forecast</h1> <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>Subject</th>
<th>Sender</th>
<th>Received Time</th>
</tr>
</thead>
<tbody>
@foreach (var mail in messages)
{
<tr>
<td>@mail.Subject</td>
<td>@mail.Sender</td>
<td>@mail.ReceivedTime</td>
</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" 页面,可以看到一些电子邮件!

如果你正确的如上演练,你现在应该可以看到类似上面图示中的你的电子邮件数据了。

总结

新的 Microsoft.Identity.Web 在简化验证和令牌管理方面做了出色的改进,你现在就应该开始使用它,在开始之前,有些事情值得你关注:

  1. 与普通的支持动态/增加提醒的 Web 应用不同,在 Blazor 中,你需要在一开始为你的应用请求所有需要的权限。失败的话,将会导致 TokenAcruisition 方法不能根据新的权限验证用户。这是 Blazor 机制的一部分,所以在创建应用的时候要记住这一点。
  2. 不是自己处理 Microsoft Graph HTTP 请求,你应该借助于 Microsoft Graph SDK,它简化了与 Microsoft Graph 的交互并提供了所有你需要序列化和反序列的对象。但是,在本示例中,我们仅仅遵循了建议 1,因为我们仅仅发出一个 Microsoft Graph 调用。

最后,你可以在 GitHub 上找到本示例 Blazor 应用程序的可运行示例的所有的源代码

参考

如何使用 Azure Active Directory 认证和 Microsoft Graph 构建 Blazor Web 应用的更多相关文章

  1. 如何通过 PHP 获取 Azure Active Directory 令牌

    在调用 Azure Rest API 时,如果是属于 Azure Resource Manager 的 API,则需要使用 Azure Active Directory (Azure AD)认证获取令 ...

  2. Azure Active Directory中的特权身份管理如何运作?

    [TechTarget中国原创] 用户权限不是平等的.有些用户需要有大量权利和特权——通常这些都是管理员.企业在允许特权用户进行管理以及支持活动时,还需要意识到特权用户也有可能犯错.他们会犯错.他们可 ...

  3. SRV记录用来标识某台服务器使用了某个服务,常见于微软系统的目录管理——深入的话需要去折腾Azure Active Directory

    SRV记录 SRV记录 什么情况下会用到SRV记录? [SRV记录用来标识某台服务器使用了某个服务,常见于微软系统的目录管理] SRV记录的添加方式 A.主机记录处格式为:服务的名字.协议的类型 例如 ...

  4. Azure AD(五)使用多租户应用程序模式让任何 Azure Active Directory 用户登录

    一,引言 距离上次分享关于 “Azure AD" 的知识过去差不多2个多月了, 今天最近刚好也是学习,分享一下关于Azure AD 使用多租户应用程序模式让任何 Azure Active D ...

  5. 【Azure Developer】使用 adal4j(Azure Active Directory authentication library for Java)如何来获取Token呢

    问题描述 使用中国区的Azure,在获取Token时候,参考了 adal4j的代码,在官方文档中,发现了如下的片段代码: ExecutorService service = Executors.new ...

  6. 简化 Web 应用程序与 Windows Azure Active Directory、ASP.NET 和 Visual Studio 的集成

    大家好! 今天的博文深入讨论我们今天推出的开发人员工具和框架中的一些新功能.我们通过与 ASP.NET 和 Visual Studio 团队合作开发了一些重大的增强功能,让开发人员能够轻松使用 Win ...

  7. 移动服务和 Azure Active Directory 中基于角色的访问控制

    编辑人员注释:本文章由 Matthew Henderson撰写 去年 11月,我们发布了 Azure Active Directory (AAD) 预览版作为移动服务身份提供程序.此举旨在为企业开 ...

  8. Azure Active Directory document ---reading notes

    微软利用本地活动目录 Windows Server Active Directory 进行身份认证管理方面具有丰富的经验,现在这一优势已延伸基于云平台的Azure Active Directory.可 ...

  9. 使用 Azure Active Directory 管理 Azure 中的 HPC Pack 群集

    Microsoft HPC Pack 2016 支持在 Azure 中部署 HPC Pack 群集的管理员将其与 Azure Active Directory (Azure AD) 集成. 请按照本文 ...

随机推荐

  1. Vue开发电子书app

    ebook-app 在根目录下创建vue.config.js文件 module.export = { baseUrl: process.env.NODE_ENV === 'production' ? ...

  2. 《Java核心技术卷一》之 泛型

    一.引言 在学习集合的时候我们会发现一个问题,将一个对象丢到集合中后,集合并不记住对象的类型,统统都当做Object处理,这样我们取出来再使用时就得强制转换类型,导致代码臃肿,而且加入集合时都是以Ob ...

  3. PowerJob 从 0 到 1.9k star 的经历

    本文适合有 Java 基础知识的人群 作者:HelloGitHub-Salieri 本文就是<讲解 PowerJob>系列的最后一篇文章了,纯粹是写点经历写点心路历程啥的,和大家道个别. ...

  4. 方法区(Method Area)基础知识

    堆.栈.方法区堆关系 概述 方法区与堆区一样,是各个线程共享的内存区域 方法区在JVM启动时就会被创建,并且它的实际的物理内存空间中和Java堆区一样都可以是不连续的 方法区的大小,跟堆空间一样,可以 ...

  5. Java12新特性

    switch表达式(预览) 传统switch的缺点 匹配是自上而下的,如果忘记写break, 后面的case语句不论匹配与否都会执行; 所有的case语句共用一个块范围,在不同的case语句定义的变量 ...

  6. 刷题[SUCTF 2018]GetShell

    解题思路 进入upload,发现有源码,代码审计 代码审计 大概意思就是,读取我们上传的文件,截取第五位之后的内容,与黑名单不匹配才能上传成功 我传的是一个空的txt文件,发现会变成php文件,那好办 ...

  7. Go 安装介绍

    Go介绍 Go语言被誉为21世纪的C语言,由Google公司开发,天生对高并发有着优秀的支持.并且语法极度简洁,关键字仅有25个. 所以使用Go语言时你不用担心自己写的和大神写的有着天差地别,Go语言 ...

  8. springmvc与mybatis整合时 java.lang.IllegalArgumentException: Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required 异常

    今天在整合springmvc与mybatis时,启动服务器遇到这样一个问题, by: java.lang.IllegalArgumentException: Property 'sqlSessionF ...

  9. 043 01 Android 零基础入门 01 Java基础语法 05 Java流程控制之循环结构 05 do-while循环介绍及应用

    043 01 Android 零基础入门 01 Java基础语法 05 Java流程控制之循环结构 05 do-while循环介绍及应用 本文知识点:do-while循环介绍及应用 do-while循 ...

  10. C++对话框创建及修改对话框属性

    转载:http://www.51testing.com/html/48/n-3151648.html 创建对话框 C++中对话框分为模式对话框和非模式对话框. 模式对话框的创建: MyDialog m ...