ASP.NET Core 6中为Azure AD添加自定义Claim字段的技术问题
问题解答
1. 能否纯代码将UserID存入Azure AD认证Cookie?
完全可以,不需要Azure AD管理面板权限。ASP.NET Core的认证系统会把ClaimsPrincipal里的所有Claim存入本地生成的认证Cookie(不是Azure AD直接发放的Cookie,是本地维护的会话Cookie)。你只要在认证流程里添加自定义Claim,就会自动被包含到Cookie中,逻辑和之前Identity框架一致。
2. 该方案对SignalR是否可行?
非常适合SignalR。SignalR无法直接使用会话状态,但会自动携带认证Cookie,连接建立时ASP.NET Core会解析Cookie生成ClaimsPrincipal,你在Hub里可以直接通过Context.User.FindFirstValue("UserID")获取这个Claim,比会话方案更可靠,完全满足需求。
3. 解决OnTokenValidated事件中无法获取preferred_username的问题
你的代码里用context.HttpContext.User获取Claim是错误的,OnTokenValidated触发时,HttpContext.User还没完成初始化。正确做法是从context.Principal(由Azure AD的ID Token生成的认证主体)中获取,或者直接解析context.SecurityToken(ID Token)。
另外,代码还有两个优化点:
- 别在事件处理器里直接创建
SqlConnection,建议通过依赖注入获取数据服务或IDbConnection实例,符合ASP.NET Core设计规范。 - 用异步方法避免阻塞线程,比如
QueryFirstAsync替代QueryFirst。
修改后的代码示例:
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApp(options => { builder.Configuration.Bind("AzureAd", options); options.Events = new OpenIdConnectEvents { OnTokenValidated = async context => { // 从context.Principal获取preferred_username string signInEmailAddress = context.Principal.FindFirstValue("preferred_username"); if (string.IsNullOrEmpty(signInEmailAddress)) { throw new InvalidOperationException("无法获取用户邮箱"); } // 通过依赖注入获取数据库连接(推荐方式) var dbConnection = context.HttpContext.RequestServices.GetRequiredService<IDbConnection>(); string query = "select dbo.A2F_0013_ReturnUserIDForEmail(@Email) as UserID"; var queryResult = await dbConnection.QueryFirstAsync(query, new { Email = signInEmailAddress }); // 添加自定义UserID Claim var appIdentity = new ClaimsIdentity(new[] { new Claim("UserID", queryResult.UserID.ToString()) }); context.Principal.AddIdentity(appIdentity); }, }; }) .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi")) .AddInMemoryTokenCaches();
额外说明
- 确保Azure AD的ID Token包含
preferred_username:默认Azure AD会返回该Claim,如果没有,可在OpenIdConnectOptions中添加options.Scope.Add("profile"),因为preferred_username属于profile范围的Claim。 - 自定义Claim会自动持久化到认证Cookie中,后续所有请求(包括SignalR连接)都能通过
HttpContext.User或Hub.Context.User获取到。
内容的提问来源于stack exchange,提问作者Qiuzman
相关产品推荐
相关产品推荐

