Microsoft Identity.Web 與 ASP.NET Core 的日誌基礎設施整合。 利用它來診斷以下問題:
- 認證流程 - 登入、登出、令牌驗證
- 令牌擷取 - 令牌快取命中/未命中,MSAL 操作
- 下游 API 呼叫 - HTTP 請求、API 權杖的取得
- 錯誤條件 - 例外、驗證失敗
了解已記錄的元件
| 組件 | 日誌來源 | Purpose |
|---|---|---|
| Microsoft。Identity.Web | 核心認證邏輯 | 設定、令牌擷取、API 呼叫 |
| MSAL.NET | Microsoft.Identity.Client |
令牌快取操作與授權驗證 |
| 身份模型 | 令牌驗證 | JWT 解析、簽名驗證、理賠擷取 |
| ASP.NET Core Auth | Microsoft.AspNetCore.Authentication |
Cookie 操作、禁用/限制操作 |
開始記錄作業
最小配置
將以下日誌層級條目新增至 appsettings.json 以啟用身份識別日誌:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Identity": "Information"
}
}
}
這使得對 Microsoft.Identity.Web 及其相依套件(MSAL.NET、IdentityModel)啟用資訊層級日誌記錄成為可能。
開發配置
關於開發過程中的詳細診斷:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Identity": "Debug",
"Microsoft.AspNetCore.Authentication": "Information"
}
},
"AzureAd": {
"EnablePiiLogging": true // Development only!
}
}
生產配置
在生產環境中,請在記錄錯誤時盡量減少日誌量。
{
"Logging": {
"LogLevel": {
"Default": "Warning",
"Microsoft": "Warning",
"Microsoft.Identity": "Warning"
}
},
"AzureAd": {
"EnablePiiLogging": false // Never true in production
}
}
設定日誌過濾
基於命名空間的過濾
控制日誌詳細程度依命名空間。 以下配置為每個與身份相關的命名空間設置細度層級:
{
"Logging": {
"LogLevel": {
"Default": "Information",
// General Microsoft namespaces
"Microsoft": "Warning",
"Microsoft.AspNetCore": "Warning",
// Identity-specific namespaces
"Microsoft.Identity": "Information",
"Microsoft.Identity.Web": "Information",
"Microsoft.Identity.Client": "Information",
// ASP.NET Core authentication
"Microsoft.AspNetCore.Authentication": "Information",
"Microsoft.AspNetCore.Authentication.JwtBearer": "Information",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "Debug",
// Token validation
"Microsoft.IdentityModel": "Warning"
}
}
}
停用特定日誌
為了在不影響其他元件的情況下靜音雜訊元件,請將其對數電平設為 None 或 Warning:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Identity.Web": "None", // Completely disable
"Microsoft.Identity.Client": "Warning" // Only errors/warnings
}
}
}
環境特定的設定
appsettings.{Environment}.json用於各環境設定:
appsettings.Development.json:
{
"Logging": {
"LogLevel": {
"Microsoft.Identity": "Debug"
}
},
"AzureAd": {
"EnablePiiLogging": true
}
}
appsettings.Production.json:
{
"Logging": {
"LogLevel": {
"Microsoft.Identity": "Warning"
}
},
"AzureAd": {
"EnablePiiLogging": false
}
}
了解日誌等級
ASP.NET Core 定義了以下日誌層級。 選擇一個能平衡診斷細節與日誌量的級別。
ASP.NET Core 日誌層級
| 等級 | 使用方式 | 音量 | 生產? |
|---|---|---|---|
| 追蹤 | 每項操作都最詳細。 | 非常高 | No |
| 偵錯 | 詳細的流程,對開發者很有用 | 高 | No |
| 資訊 | 整體流程與關鍵事件 | 溫和 | 選擇性 |
| 警告 | 意外但已妥善處理的狀況 | 低 | 是的 |
| 錯誤 | 錯誤和例外狀況 | 非常低 | 是的 |
| 重要 | 無法恢復的故障 | 非常低 | 是的 |
| 沒有 | 停用日誌 | 沒有 | 選擇性 |
將 MSAL.NET 映射到 ASP.NET Core 層級
| MSAL.NET 級別 | ASP.NET Core 等價物 | 說明 |
|---|---|---|
Verbose |
Debug 或 Trace |
最詳細的訊息 |
Info |
Information |
金鑰認證事件 |
Warning |
Warning |
異常但已處理的狀況 |
Error |
Error 或 Critical |
錯誤和例外狀況 |
依環境套用建議設定
請依照每個環境使用以下配置。
發展:
{
"Logging": {
"LogLevel": {
"Microsoft.Identity": "Debug",
"Microsoft.Identity.Client": "Information"
}
}
}
舞台設計:
{
"Logging": {
"LogLevel": {
"Microsoft.Identity": "Information",
"Microsoft.Identity.Client": "Warning"
}
}
}
製作:
{
"Logging": {
"LogLevel": {
"Microsoft.Identity": "Warning",
"Microsoft.Identity.Client": "Error"
}
}
}
設定個人識別資訊(PII)記錄
預設是 Microsoft。Identity.Web 會從日誌中刪除個人識別資訊(PII)。 僅在開發環境中啟用個人識別資訊(PII)記錄,以查看完整使用者資料。
什麼是個人識別資訊(PII)?
個人識別資訊(PII) 包括:
- 用戶名稱、電子郵件地址
- 顯示名稱
- 物件 ID、租戶 ID
- IP 位址
- 代幣價值與主張
安全性警告
警告:您和您的應用程式需自行遵守所有適用的法規要求,包括 GDPR所規定的規定。 在啟用個人識別資料記錄前,請確保你能安全處理這些可能高度敏感的資料。
啟用個人識別資訊(PII)紀錄(僅限開發階段)
在你的開發設定檔中設定 EnablePiiLogging 為 true :
appsettings.Development.json:
{
"AzureAd": {
"EnablePiiLogging": true // Development/Testing ONLY
},
"Logging": {
"LogLevel": {
"Microsoft.Identity": "Debug"
}
}
}
以程式方式控制個人識別資訊(PII)記錄
根據主機環境切換 PII 記錄:
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<MicrosoftIdentityOptions>(options =>
{
// Only enable PII in Development
options.EnablePiiLogging = builder.Environment.IsDevelopment();
});
啟用個人識別碼後會有什麼改變?
不記錄個人身份資訊(PII):
[Information] Token validation succeeded for user '{hidden}'
[Information] Acquired token from cache for scopes '{hidden}'
啟用個人識別資訊後:
[Information] Token validation succeeded for user 'john.doe@contoso.com'
[Information] Acquired token from cache for scopes 'user.read api://my-api/.default'
日誌中的個人識別資訊(PII)刪除
當 PII 記錄被停用時,敏感資料會被替換為:
-
{hidden}- 隱藏使用者識別碼 -
{hash:XXXX}- 顯示雜湊值而非實際值 -
***- 遮蔽標記
使用相關性識別碼
相關識別碼追蹤跨服務的認證請求。 將這些資訊納入日誌和支援工單,以加快問題解決。
什麼是相關識別碼?
相關 ID 是一種 GUID ,能唯一識別以下區域的認證或憑證取得請求:
- 您的應用程式
- Microsoft Identity 平台
- MSAL.NET 函式庫
- Microsoft 後端服務
取得相關識別碼
方法一:來自 AuthenticationResult
成功取得代幣後,從中 AuthenticationResult 提取相關ID:
using Microsoft.Identity.Web;
public class TodoController : ControllerBase
{
private readonly ITokenAcquisition _tokenAcquisition;
private readonly ILogger<TodoController> _logger;
public TodoController(
ITokenAcquisition tokenAcquisition,
ILogger<TodoController> logger)
{
_tokenAcquisition = tokenAcquisition;
_logger = logger;
}
[HttpGet]
public async Task<IActionResult> GetTodos()
{
var result = await _tokenAcquisition.GetAuthenticationResultForUserAsync(
new[] { "user.read" });
_logger.LogInformation(
"Token acquired. CorrelationId: {CorrelationId}, Source: {TokenSource}",
result.CorrelationId,
result.AuthenticationResultMetadata.TokenSource);
return Ok(result.CorrelationId);
}
}
方法二:來自 MsalServiceException
擷取當代幣取得失敗時的關聯 ID MsalServiceException:
using Microsoft.Identity.Client;
try
{
var token = await _tokenAcquisition.GetAccessTokenForUserAsync(
new[] { "user.read" });
}
catch (MsalServiceException ex)
{
_logger.LogError(ex,
"Token acquisition failed. CorrelationId: {CorrelationId}, ErrorCode: {ErrorCode}",
ex.CorrelationId,
ex.ErrorCode);
// Return correlation ID to user for support
return StatusCode(500, new {
error = "authentication_failed",
correlationId = ex.CorrelationId
});
}
方法三:設定自訂關聯 ID
指派自訂關聯 ID 來連結應用程式追蹤與 Microsoft Entra ID 請求:
[HttpGet("{id}")]
public async Task<IActionResult> GetTodo(int id)
{
// Use request trace ID as correlation ID
var correlationId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
var todo = await _downstreamApi.GetForUserAsync<Todo>(
"TodoListService",
options =>
{
options.RelativePath = $"api/todolist/{id}";
options.TokenAcquisitionOptions = new TokenAcquisitionOptions
{
CorrelationId = Guid.Parse(correlationId)
};
});
_logger.LogInformation(
"Called downstream API. TraceId: {TraceId}, CorrelationId: {CorrelationId}",
HttpContext.TraceIdentifier,
correlationId);
return Ok(todo);
}
提供相關ID以供支援
聯絡 Microsoft 客服時,請提供以下資訊:
- 相關性 ID - 來自日誌或例外
- 時間戳 記 - 錯誤發生時間(UTC)
- 租戶編號 - 您的Microsoft Entra ID租戶
-
錯誤代碼 - 如適用(例如,
AADSTS50058)
支援請求範例:
Subject: Token acquisition failing for user.read scope
Correlation ID: 12345678-1234-1234-1234-123456789012
Timestamp: 2025-01-15 14:32:45 UTC
Tenant ID: contoso.onmicrosoft.com
Error Code: AADSTS50058
啟用令牌快取記錄
令牌快取記錄幫助你了解快取的命中/失行為,並診斷分散式快取的效能問題。
啟用令牌快取診斷
對於使用分散式令牌快取的 .NET Framework 或 .NET Core 應用程式,請設定詳細記錄:
using Microsoft.Extensions.Logging;
using Microsoft.Identity.Web.TokenCacheProviders;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDistributedTokenCaches();
// Enable detailed token cache logging
builder.Services.AddLogging(configure =>
{
configure.AddConsole();
configure.AddDebug();
})
.Configure<LoggerFilterOptions>(options =>
{
options.MinLevel = LogLevel.Debug; // Detailed cache operations
});
令牌快取日誌範例
快取命中:
[Debug] Token cache: Token found in cache for scopes 'user.read'
[Information] Token source: Cache
快取未命中:
[Debug] Token cache: No token found in cache for scopes 'user.read'
[Information] Token source: IdentityProvider
[Debug] Token cache: Token stored in cache
排除分散式快取問題
啟用提供者專屬的日誌功能,以診斷快取連線與效能問題。
Redis 快取:
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration["Redis:ConnectionString"];
});
// Enable Redis logging
builder.Services.AddLogging(configure =>
{
configure.AddFilter("Microsoft.Extensions.Caching", LogLevel.Debug);
});
SQL Server cache:
配置帶有日誌功能的 SQL Server 分散式快取:
builder.Services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = builder.Configuration["SqlCache:ConnectionString"];
options.SchemaName = "dbo";
options.TableName = "TokenCache";
});
// Enable SQL cache logging
builder.Services.AddLogging(configure =>
{
configure.AddFilter("Microsoft.Extensions.Caching.SqlServer", LogLevel.Information);
});
排除常見問題
請使用以下情境來診斷頻繁的認證與授權問題。
常見的伐木情境
情境一:令牌驗證失敗
症狀: 401 未經授權的回應
啟用詳細記錄:
{
"Logging": {
"LogLevel": {
"Microsoft.AspNetCore.Authentication.JwtBearer": "Debug",
"Microsoft.IdentityModel": "Information"
}
}
}
尋找:
[Information] Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler:
Failed to validate the token.
[Debug] Microsoft.IdentityModel.Tokens: IDX10230: Lifetime validation failed.
The token is expired.
情境二:代幣取得失敗
症狀:MsalServiceException 或 MsalUiRequiredException
啟用詳細記錄:
{
"Logging": {
"LogLevel": {
"Microsoft.Identity.Web": "Debug",
"Microsoft.Identity.Client": "Information"
}
}
}
尋找:
[Error] Microsoft.Identity.Web: Token acquisition failed.
ErrorCode: invalid_grant, CorrelationId: {guid}
[Information] Microsoft.Identity.Client: MSAL returned exception:
AADSTS50058: Silent sign-in failed.
情境三:下游 API 呼叫失敗
症狀: HTTP 502 或呼叫下游 API 時的逾時錯誤
啟用詳細記錄:
{
"Logging": {
"LogLevel": {
"Microsoft.Identity.Abstractions": "Debug",
"System.Net.Http": "Information"
}
}
}
在控制器中加入自訂日誌以捕捉下游 API 錯誤:
[HttpGet]
public async Task<IActionResult> GetUserProfile()
{
try
{
_logger.LogInformation("Acquiring token for Microsoft Graph");
var user = await _downstreamApi.GetForUserAsync<User>(
"MicrosoftGraph",
options => options.RelativePath = "me");
_logger.LogInformation(
"Successfully retrieved user profile for {UserPrincipalName}",
user.UserPrincipalName);
return Ok(user);
}
catch (MsalUiRequiredException ex)
{
_logger.LogWarning(ex,
"User interaction required. CorrelationId: {CorrelationId}",
ex.CorrelationId);
return Challenge();
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Failed to call Microsoft Graph API");
return StatusCode(502, "Downstream API error");
}
}
解讀對數模式
以下範例展示了常見認證事件的典型日誌輸出。
認證流程成功:
[Info] Authentication scheme OpenIdConnect: Authorization response received
[Debug] Correlation id: {guid}
[Info] Authorization code received
[Info] Token validated successfully
[Info] Authentication succeeded for user: {user}
需同意:
[Warning] Microsoft.Identity.Web: Incremental consent required
[Info] AADSTS65001: User consent is required for scopes: {scopes}
[Info] Redirecting to consent page
代幣更新:
[Debug] Token expired, attempting silent token refresh
[Info] Token source: IdentityProvider
[Info] Token refreshed successfully
與外部供應商一起聚合日誌
將身份日誌轉發至集中式日誌平台以進行監控與警示。
Application Insights 整合:
將識別遙測資料傳送至 Application Insights,並增強其相關性 ID:
using Microsoft.ApplicationInsights.Extensibility;
builder.Services.AddApplicationInsightsTelemetry();
// Enrich telemetry with correlation IDs
builder.Services.AddSingleton<ITelemetryInitializer, CorrelationIdTelemetryInitializer>();
Serilog 整合:
設定 Serilog 將身份日誌擷取至主控台及滾動檔案輸出:
using Serilog;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft.Identity", Serilog.Events.LogEventLevel.Debug)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File("logs/identity-.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
builder.Host.UseSerilog();
遵循伐木最佳實務
運用這些做法,確保您的身份記錄安全、實用且具效能。
應該做的事
1. 使用結構化記錄:
將數值以命名參數傳遞,讓日誌聚合器能夠索引並查詢:
_logger.LogInformation(
"Token acquired for user {UserId} with scopes {Scopes}",
userId, string.Join(" ", scopes));
2. 日誌相關ID:
錯誤日誌中請務必包含相關ID,以簡化支援調查:
_logger.LogError(ex,
"Operation failed. CorrelationId: {CorrelationId}",
ex.CorrelationId);
3. 使用適當的日誌級別:
將日誌級別與嚴重程度及受眾對應:
_logger.LogDebug("Detailed diagnostic info"); // Development
_logger.LogInformation("Key application events"); // Selective production
_logger.LogWarning("Unexpected but handled"); // Production
_logger.LogError(ex, "Operation failed"); // Production
4. 在生產環境中清洗日誌:
在寫入生產日誌前,先遮蔽敏感值:
var sanitizedEmail = environment.IsProduction()
? MaskEmail(email)
: email;
_logger.LogInformation("Processing request for {Email}", sanitizedEmail);
禁忌事项
1. 不要在生產環境中啟用個人識別資訊(PII):
// Wrong
"EnablePiiLogging": true // In production config!
// Correct
"EnablePiiLogging": false
2. 不要記錄秘密:
// Wrong
_logger.LogInformation("Token: {Token}", accessToken);
// Correct
_logger.LogInformation("Token acquired, expires: {ExpiresOn}", expiresOn);
3. 不要在生產環境中使用冗長日誌:
// Wrong - production appsettings.json
"Microsoft.Identity": "Debug"
// Correct
"Microsoft.Identity": "Warning"