如何使用 Azure Active Directory 認證和 Microsoft Graph 構建 Blazor Web 應用
- 2020 年 10 月 24 日
- 筆記
- .NET, Asp.Net, ASP.NET MVC, Bootstrap, dotnet core
如何使用 Azure Active Directory 認證和 Microsoft Graph 構建 Blazor Web 應用
如果您是一個 .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("//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("//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 在簡化驗證和令牌管理方面做了出色的改進,你現在就應該開始使用它,在開始之前,有些事情值得你關註:
- 與普通的支援動態/增加提醒的 Web 應用不同,在 Blazor 中,你需要在一開始為你的應用請求所有需要的許可權。失敗的話,將會導致 TokenAcruisition 方法不能根據新的許可權驗證用戶。這是 Blazor 機制的一部分,所以在創建應用的時候要記住這一點。
- 不是自己處理 Microsoft Graph HTTP 請求,你應該藉助於 Microsoft Graph SDK,它簡化了與 Microsoft Graph 的交互並提供了所有你需要序列化和反序列的對象。但是,在本示例中,我們僅僅遵循了建議 1,因為我們僅僅發出一個 Microsoft Graph 調用。
最後,你可以在 GitHub 上找到本示例 Blazor 應用程式的可運行示例的所有的源程式碼,