认证后存储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
相关产品推荐
相关产品推荐

