共用方式為


使用 Serilog 來排除 Microsoft Entra 保護的 Web API 驗證或授權錯誤的故障。

當 Web API 應用程式呼叫受 Microsoft Entra 識別碼保護的 Web API 時,可能會因為 JwtBearer 事件驗證失敗而發生驗證或授權錯誤。 為了針對此問題進行疑難解答,本文介紹名為 Net6WebAPILogging 的範例 Web API 應用程式,以設定及收集 JwtBearer 事件的記錄。

Net6WebAPILogging 範例應用程式

此範例 Web API 應用程式假設您已經在 Microsoft Entra ID 註冊了 Web API。 它會使用 Microsoft .NET 6 Framework 和 Microsoft Identity Web NuGet 套件。

它會使用下列方法來設定及收集 JwtBearer 事件的記錄:

  • 使用 JwtBearerEvents 類別 來設定中間件事件。 JWT 持有人令牌可能無法驗證 OnTokenValidatedOnMessageReceivedOnAuthenticationFailedOnChalleenge 事件。
  • 設定 JwtBearer 事件的記錄
  • 使用 Serilog 框架,將輸出紀錄到主控台視窗和在 appsettings.json 檔案中指定路徑的本機檔案。

執行範例應用程式

若要執行範例應用程式,您必須執行下列步驟:

步驟 1:設定受保護 Web API 的應用程式識別碼 URI

若要新增 Web API 的應用程式識別碼 URI,請遵循下列步驟:

  1. 在 Azure 入口網站中,流覽至 Web API 的應用程式註冊。

  2. 選取 [公開 API] 底下的 [管理]。

  3. 在頁面頂端,選取 [應用程式識別碼 URI] 旁邊的 [新增]。 預設值為 api://<application-client-id>

    顯示如何在應用程式註冊中設定應用程式識別碼 URI 的螢幕快照。

  4. 選取 [儲存]。

步驟 2:變更範例應用程式組態

appsettings.json檔案的AzureAd區段中,使用您自己的應用程式註冊資訊變更下列資訊:

"AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "<tenant name>.onmicrosoft.com", // for example contoso.onmicrosoft.com
    "TenantId": "<tenant ID>",
    "ClientId": "<application-client-id>"
  },

步驟 3:變更範例應用程式程式代碼

變更 Program.cs 檔案中 TokenValidationParameters 類別的 ValidAudiencesValidIssuers 屬性。

步驟 4:設定 Serilog

Serilogappsettings.json 檔案的 區段中設定 Serilog,如下所示:

  "Serilog": {
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Debug",
        "Microsoft.Hosting.Lifetime": "Information"
      }
    },

設定 JwtBearer 事件的日誌記錄

以下是範例 Program.cs 檔案,示範如何設定上述事件的記錄:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
using Microsoft.IdentityModel.Logging;
using System.Diagnostics;
using Serilog;


// https://github.com/datalust/dotnet6-serilog-example

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .CreateBootstrapLogger();

Log.Information("starting up");
try
{
    var builder = WebApplication.CreateBuilder(args);

    builder.Host.UseSerilog((ctx, lc) => lc
        .WriteTo.Console()
        .ReadFrom.Configuration(ctx.Configuration));

    // Add services to the container.
    builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));

    // Enable PII for logging
    IdentityModelEventSource.ShowPII = true;
    // Configure middleware events
    builder.Services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
        {
            ValidAudiences = new List<string> { "api://<application-client-id>", "<application-client-id>" },
            ValidIssuers = new List<string> { "https://sts.windows.net/<tenant ID>/", "https://login.microsoftonline.com/<tenant ID>/v2.0" }
        };
        options.Events = new JwtBearerEvents
        {
            OnTokenValidated = ctx =>
            {
                string message = "[OnTokenValidated]: ";
                message += $"token: {ctx.SecurityToken.ToString()}";
                Log.Information(message); 
                return Task.CompletedTask;
            },
            OnMessageReceived = ctx =>
            {
                string message = "[OnMessageReceived]: ";
                ctx.Request.Headers.TryGetValue("Authorization", out var BearerToken);
                if (BearerToken.Count == 0)
                    BearerToken = "no Bearer token sent\n";
                message += "Authorization Header sent: " + BearerToken + "\n";
                Log.Information(message);
                return Task.CompletedTask;
            },
            OnAuthenticationFailed = ctx =>
            {
                ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
                string message = $"[OnAuthenticationFailed]: {ctx.Exception.ToString()}";
                Log.Error(message);
                // Debug.WriteLine("[OnAuthenticationFailed]: Authentication failed with the following error: ");
                // Debug.WriteLine(ctx.Exception);
                return Task.CompletedTask;
            },
            OnChallenge = ctx =>
            {
                // Debug.WriteLine("[OnChallenge]: I can do stuff here! ");
                Log.Information("[OnChallenge]");
                return Task.CompletedTask;
            },
            OnForbidden = ctx =>
            {
                Log.Information("[OnForbidden]");
                return Task.CompletedTask;
            }
        };
    });

    builder.Services.AddControllers();
    // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
    // builder.Services.AddEndpointsApiExplorer();
    // builder.Services.AddSwaggerGen();

    var app = builder.Build();

    app.UseSerilogRequestLogging();
    // Configure the HTTP request pipeline.
    if (app.Environment.IsDevelopment())
    {
        // app.UseSwagger();
        // app.UseSwaggerUI();
        // do something
    }

    app.UseHttpsRedirection();

    app.UseAuthentication();
    app.UseAuthorization();

    app.MapControllers();

    app.Run();
}
catch (Exception ex)
{
    Log.Fatal(ex, "Unhandled exception");
}
finally
{
    Log.Information("Shut down complete");
    Log.CloseAndFlush();
}

參考資料

教學課程:使用 Microsoft 身分識別平臺建置及保護 ASP.NET Core Web API

第三方資訊免責聲明

本文提及的協力廠商產品是由與 Microsoft 無關的獨立廠商所製造。 Microsoft對這些產品的性能或可靠性不作明示或暗示的保證。

與我們連絡,以取得說明

如果您有任何問題或需要幫助,提出支援請求,或詢問Azure 社群支援。 您也可以向 Azure 意見反應社群提交產品意見反應。