You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

认证后存储DisplayName并在DataContext中使用的问题排查

问题:无法从ClaimsIdentity获取DisplayName值,导致InsertUser/UpdateUser无法赋值为登录用户

我正在构建WebAPI,模型包含InsertUser、UpdateUser等元数据列,使用Microsoft.Identity做用户认证。希望认证成功后调用Graph获取用户DisplayName并存入Claims,在DataContext.SaveChanges()中为实体元数据列赋值,但测试时获取到的DisplayName为空。

错误排查与解决方案

1. 核心问题:Graph调用权限或Token获取失败

你的代码中调用Graph的/me端点时,可能存在权限未配置或Token获取逻辑失效的问题,导致无法获取到用户DisplayName,最终存入Claims的值为空。

修复步骤:

(1)配置Azure AD应用权限
  • 在Azure AD应用注册中,添加Delegated权限:User.Read,并完成管理员同意。
  • 确保appsettings.json的MicrosoftGraph配置正确:
"MicrosoftGraph": {
  "BaseUrl": "https://graph.microsoft.com/v1.0",
  "Scopes": "User.Read"
}
(2)修正Graph调用的Token获取逻辑

在OnTokenValidated事件中,GraphServiceClient可能无法自动获取有效Token,需手动通过ITokenAcquisition获取:

services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
    options.Events ??= new JwtBearerEvents
    {
        OnTokenValidated = async context =>
        {
            var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>();
            var graphClient = new GraphServiceClient(
                new DelegateAuthenticationProvider(async (requestMessage) =>
                {
                    // 获取调用User.Read权限的Token
                    var accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(new[] { "User.Read" });
                    requestMessage.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
                })
            );

            var user = await graphClient.Me.Request().GetAsync();
            var identity = context.Principal.Identity as ClaimsIdentity;
            if (identity != null && !string.IsNullOrEmpty(user.DisplayName))
            {
                identity.AddClaim(new Claim("DisplayName", user.DisplayName));
            }
        }
    };
});

2. 优化方案:直接从JWT Token提取DisplayName(无需调用Graph)

Azure AD的JWT Token默认包含name或http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name声明,可直接提取,避免额外Graph请求:

options.Events ??= new JwtBearerEvents
{
    OnTokenValidated = context =>
    {
        var identity = context.Principal.Identity as ClaimsIdentity;
        if (identity != null)
        {
            // 从现有Claims中提取DisplayName
            var displayName = context.Principal.FindFirstValue(ClaimTypes.Name) 
                              ?? context.Principal.FindFirstValue("name")
                              ?? "System";
            identity.AddClaim(new Claim("DisplayName", displayName));
        }
        return Task.CompletedTask;
    }
};

3. 完善DataContext中用户信息的获取

注入IHttpContextAccessor,在SaveChanges中直接获取当前用户DisplayName:

public class YourDataContext : DbContext
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public YourDataContext(DbContextOptions<YourDataContext> options, IHttpContextAccessor httpContextAccessor)
        : base(options)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public override int SaveChanges()
    {
        var currentUser = _httpContextAccessor.HttpContext?.User?.FindFirstValue("DisplayName") ?? "System";
        var entries = ChangeTracker
            .Entries()
            .Where(e => e.Entity is BaseEntity && (
                    e.State == EntityState.Added
                    || e.State == EntityState.Modified));

        foreach (var entityEntry in entries)
        {
            var baseEntity = (BaseEntity)entityEntry.Entity;
            baseEntity.UpdateTimestamp = DateTime.UtcNow;
            baseEntity.UpdateUser = currentUser;

            if (entityEntry.State == EntityState.Added)
            {
                baseEntity.InsertTimestamp = DateTime.UtcNow;
                baseEntity.InsertUser = currentUser;
            }
        }

        return base.SaveChanges();
    }
}

同时确保注册IHttpContextAccessor:

services.AddHttpContextAccessor();

4. 修正Claim获取代码

使用更简洁的FindFirstValue方法获取Claim:

var userProfileDisplayName = _httpContextAccessor.HttpContext?.User?.FindFirstValue("DisplayName");
Console.WriteLine($"User: {userProfileDisplayName}");

内容的提问来源于stack exchange,提问作者Andrei Budaes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 22:25:39