如何构建基于Azure AD认证和Microsoft Graph的简单Blazor Web应用

作为.NET开发者,我们都听说最近Blazor比较火。Blazor是一个使用.NET构建交互式客户端Web UI的框架。我们将在本篇博客中重点介绍Blazor Server,它提供了在ASP.NET Core应用程序中在服务器托管Razor组件的支持。通过SignalR连接更新UI。本篇示例的授权认证的部分就利用Azure AD来做,并会从Microsoft Graph获取数据。

前提条件

安装.NET Core 3.1 SDKVisual Studio 2019或其他偏好的.NET IDE,还有就是Azure AD的租户,推荐使用免费的Microsoft 365开发者订阅

Blazor和身份认证

打开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": "<your tenant id>",
    "ClientId": "<your client id>",
    "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版本的终结点。

用Microsoft.Identity.Web实现现代化的认证

首先,从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 out</a>
    </Authorized>
    <NotAuthorized>
        <a href="MicrosoftIdentity/Account/SignIn">Log in</a>
    </NotAuthorized>
</AuthorizeView>

大家应该注意到了,并没有专门的登录登出页面,因为它们被包含在Microsoft.Identity.Web这个dll中了。因此我们只需要将链接部分更新为“MicrosoftIdentity”,不需要其他更改了。

再次从Visual Studio启动我们的应用,如果我们的用户启用了更多的安全认证方式,那么就能够看到已经是V2版本的体验了。
在这里插入图片描述
如上面介绍的,我们只需要几行代码就可以使用Microsoft.Identity.Web库进行Azure AD的认证。

从Microsoft Graph获取数据

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 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页面,可以看到当前用户的邮件列表了。
在这里插入图片描述
这里如果出错就注销重新登录一下,要确保为应用更新的权限被批准。

猜你喜欢

转载自blog.csdn.net/FoxDave/article/details/107628249
今日推荐