Microsoft Identity.Web 与 ASP.NET Core 的日志基础设施集成。 使用它来诊断以下领域的问题:
- 身份验证流 - 登录、注销、令牌验证
- 令牌获取 - 令牌缓存命中/未命中、MSAL 操作
- 下游 API 调用 - HTTP 请求、API 令牌获取
- 错误条件 - 异常、验证失败
了解日志组件
| 组件 | 日志源 | Purpose |
|---|---|---|
| Microsoft。Identity.Web | 核心身份验证逻辑 | 配置、令牌获取、API 调用 |
| MSAL.NET | Microsoft.Identity.Client |
令牌缓存操作,授权验证 |
| 身份模型 | 令牌验证 | JWT 分析、签名验证、声明提取 |
| ASP.NET Core 身份验证 | 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日志级别
| 级别 | 用法 | 音量 | 生产? |
|---|---|---|---|
| Trace | 最详细地描述每个操作 | 非常高 | 否 |
| 调试 | 详细流程,适用于开发使用 | 高 | 否 |
| 信息 | 常规流程,关键事件 | 中等 | 选择性 |
| 警告 | 意外但已处理的情况 | 低 | 是的 |
| Error | 错误和异常 | 非常低 | 是的 |
| 危急 | 无法恢复的失败 | 非常低 | 是的 |
| 没有 | 禁用日志记录 | 没有 | 选择性 |
将 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 日志记录
根据托管环境开启或关闭个人信息日志记录:
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<MicrosoftIdentityOptions>(options =>
{
// Only enable PII in Development
options.EnablePiiLogging = builder.Environment.IsDevelopment();
});
启用 PII 后发生了哪些变化?
没有 PII 日志记录:
[Information] Token validation succeeded for user '{hidden}'
[Information] Acquired token from cache for scopes '{hidden}'
启用 PII:
[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 跨服务跟踪身份验证请求。 将它们包含在日志和支持票证中,以加快问题解决速度。
什么是关联 ID?
关联标识符是一个 GUID,用于唯一标识身份验证或令牌获取请求,适用于以下情况:
- 您的应用程序
- Microsoft标识平台
- MSAL.NET 库
- Microsoft后端服务
获取关联 ID
方法 1:从 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);
}
}
方法 2:来自 MsalServiceException
在令牌获取失败时从MsalServiceException捕获关联 ID:
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
});
}
方法 3:设置自定义关联 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)
- Tenant ID - 您的 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);
});
排查常见问题
使用以下方案诊断频繁的身份验证和授权问题。
常见日志记录场景
方案 1:令牌验证失败
症状: 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.
场景 2:令牌获取失败
症状: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.
方案 3:下游 API 调用失败
症状: 调用下游 API 的 HTTP 502 或超时错误
启用详细日志记录:
{
"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 集成:
通过关联 ID 增强将标识遥测发送到 Application Insights:
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"