在 Microsoft.Identity.Web 中配置日志记录。

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"
    }
  }
}

禁用特定日志记录

若要在不影响他人的情况下静音干扰组件,请将其日志级别设置为 NoneWarning

{
  "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 DebugTrace 最详细的消息
Info Information 密钥身份验证事件
Warning Warning 异常但已处理的条件
Error ErrorCritical 错误和异常

根据环境使用以下配置。

发展:

{
  "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支持人员时,请提供以下详细信息:

  1. 相关 ID - 来自日志或异常
  2. 时间戳 - 发生错误时 (UTC)
  3. Tenant ID - 您的 Microsoft Entra ID 的一个租户
  4. 错误代码 - 如果适用(例如 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:令牌获取失败

症状:MsalServiceExceptionMsalUiRequiredException

启用详细日志记录:

{
  "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"