當您呼叫使用 Microsoft Entra ID 驗證保護的 ASP.NET Core Web API 時,可能會遇到「401 未經授權」錯誤。 本文提供使用 JwtBearerEvents
來擷取詳細記錄以針對這些錯誤進行疑難解答的指引。
癥狀
您可以使用 [Authorize]
屬性來保護 您的 ASP.NET Core Web API,如下所示:
[Authorize]
public class MyController : ControllerBase
{
...
}
或
public class MyController : ControllerBase
{
[Authorize]
public ActionResult<string> Get(int id)
{
return "value";
}
...
}
當您呼叫 Web API 時,會傳回「401 未經授權」回應,但訊息不包含錯誤詳細數據。
原因
在下列案例中,API 可能會傳回「401 未經授權」回應:
- 要求不包含有效的「授權:持有人」令牌標頭。
- 令牌已過期或錯誤:
- 令牌會針對不同的資源發出。
- 令牌宣告不符合應用程式的令牌驗證準則,如 JwtBearerOptions.TokenValidationParameters 類別中所定義。
解決方法
若要偵錯並解決「401 未經授權」錯誤,請使用 JwtBearerEvents
回呼來擷取並記錄詳細的錯誤資訊。 請遵循下列步驟來實作自定義錯誤處理機制。
類別 JwtBearerEvents
具有下列回呼屬性(依下列順序叫用),可協助您偵錯這些「401 拒絕存取」或「未授權」問題:
-
OnMessageRecieved
會先被呼叫來處理每個要求。 - 如果權杖未通過應用程式的權杖驗證準則,則會呼叫
OnAuthenticationFailed
。 -
OnChallenge
在傳回 401 回應之前是最後被呼叫的。
步驟 1:啟用個人可識別信息(PII)記錄
根據預設,會停用個人標識資訊 (PII) 記錄。 在 Startup.cs 檔案的 Configure 方法中啟用它以進行偵錯。
謹慎
只有在開發環境中,才使用 'Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true'進行偵錯。 請勿在生產環境中使用它。
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You might want to change this value for production scenarios. See https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
// turn on PII logging
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseMvc();
}
步驟 2:建立公用程式方法來格式化例外狀況訊息
新增方法來格式化,並扁平化任何例外狀況訊息,以提升可讀性:
public static string FlattenException(Exception exception)
{
var stringBuilder = new StringBuilder();
while (exception != null)
{
stringBuilder.AppendLine(exception.Message);
stringBuilder.AppendLine(exception.StackTrace);
exception = exception.InnerException;
}
return stringBuilder.ToString();
}
步驟 3:實作 JwtBearerEvents 回調
在 Startup.cs 的ConfigureServices
方法中配置JwtBearerEvents
回呼,來處理身份驗證事件並紀錄錯誤詳情:
public void ConfigureServices(IServiceCollection services)
{
....
.AddJwtBearer(options =>
{
options.Authority = "https://login.microsoftonline.com/<Tenant>.onmicrosoft.com";
// if you intend to validate only one audience for the access token, you can use options.Audience instead of
// using options.TokenValidationParameters which allow for more customization.
// options.Audience = "10e569bc5-4c43-419e-971b-7c37112adf691";
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidAudiences = new List<string> { "<Application ID URI>", "10e569bc5-4c43-419e-971b-7c37112adf691" },
ValidIssuers = new List<string> { "https://sts.windows.net/<Directory ID>/", "https://sts.windows.net/<Directory ID>/v2.0" }
};
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = ctx =>
{
ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
message += "From OnAuthenticationFailed:\n";
message += FlattenException(ctx.Exception);
return Task.CompletedTask;
},
OnChallenge = ctx =>
{
message += "From OnChallenge:\n";
ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
ctx.Response.ContentType = "text/plain";
return ctx.Response.WriteAsync(message);
},
OnMessageReceived = ctx =>
{
message = "From OnMessageReceived:\n";
ctx.Request.Headers.TryGetValue("Authorization", out var BearerToken);
if (BearerToken.Count == 0)
BearerToken = "no Bearer token sent\n";
message += "Authorization Header sent: " + BearerToken + "\n";
return Task.CompletedTask;
},
#For completeness, the sample code also implemented the OnTokenValidated property to log the token claims. This method is invoked when authentication is successful
OnTokenValidated = ctx =>
{
Debug.WriteLine("token: " + ctx.SecurityToken.ToString());
return Task.CompletedTask;
}
};
});
...
}
範例結果
當您實作 JwtBearerEvents
回呼時,如果發生「401 未經授權」錯誤,響應輸出應包含如以下範例所示的詳細資訊:
OnMessageRecieved:
Authorization Header sent: no Bearer token sent.
如果您使用 API 開發工具來偵錯要求,您應該會收到錯誤詳細數據,如下列螢幕快照所示。
與我們連絡,以取得說明
如果您有任何問題或需要幫助,提出支援請求,或詢問Azure 社群支援。 您也可以向 Azure 意見反應社群提交產品意見反應。