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

ASP.NET MVC用MS Identity对接Azure AD认证后如何初始化用户会话

Azure AD认证后会话初始化最佳方案

不要把初始化逻辑写在自定义SignIn() Action里——Azure AD完成认证后是由认证中间件直接处理令牌校验、跳转逻辑,默认不会进入你自定义的SignIn业务代码,加上页面缓存的影响确实会出现逻辑不执行的问题,推荐按下面两层方案实现:

1. 核心初始化逻辑绑定OpenID Connect的OnTokenValidated事件

这个事件会在Azure AD返回的身份令牌验证通过、请求路由到任何控制器/Action之前触发,是执行一次性认证后初始化的最可靠位置,完全不受缓存、默认跳转规则影响。
在配置Azure AD认证的代码段中绑定事件即可,示例代码(ASP.NET Core):

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(options =>
    {
        // 绑定你自己的Azure AD配置参数:Instance、TenantId、ClientId等
        Configuration.Bind("AzureAd", options);
        options.Events = new OpenIdConnectEvents
        {
            OnTokenValidated = async context =>
            {
                // 直接从事件参数获取已认证的用户身份,不要用ClaimsPrincipal.Current避免异步上下文丢失
                var principal = context.Principal;
                var userId = principal.FindFirstValue("http://schemas.microsoft.com/identity/claims/objectidentifier");
                var userName = principal.FindFirstValue("name");

                // 获取用户所属组:两种方式二选一
                // 方式1:提前在Azure AD应用注册中配置返回groups声明,直接从令牌取
                // var groups = principal.FindAll("groups").Select(c => c.Value).ToList();
                // 方式2:调用Microsoft Graph接口查询用户组,适合组数量多、令牌装不下所有组声明的场景
                var groups = await QueryUserGroupsFromGraph(userId);

                // 写入当前请求会话
                var ctx = context.HttpContext;
                ctx.Session.SetString("CurrentUserId", userId);
                ctx.Session.SetString("CurrentUserName", userName);
                ctx.Session.SetString("CurrentUserGroups", JsonSerializer.Serialize(groups));
            }
        };
    });

注意:如果用Graph查询组信息,建议加上分布式缓存,以用户唯一ID为key缓存组信息,减少重复接口调用。

2. 加全局过滤器做兜底校验

为了避免服务重启会话丢失、Cookie有效期内会话过期等特殊场景下会话信息缺失,注册一个全局Action过滤器,每次请求进入Action前做校验:

  • 若用户已认证、但会话中不存在用户ID/组信息,自动重新拉取信息补全会话,不需要用户重新登录

示例过滤器代码:

public class SessionInitFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        var httpCtx = context.HttpContext;
        // 已认证但会话没初始化的场景才补全
        if (httpCtx.User.Identity.IsAuthenticated 
            && string.IsNullOrEmpty(httpCtx.Session.GetString("CurrentUserId")))
        {
            var userId = httpCtx.User.FindFirstValue("http://schemas.microsoft.com/identity/claims/objectidentifier");
            var userName = httpCtx.User.FindFirstValue("name");
            var groups = QueryUserGroupsFromGraph(userId).GetAwaiter().GetResult();

            httpCtx.Session.SetString("CurrentUserId", userId);
            httpCtx.Session.SetString("CurrentUserName", userName);
            httpCtx.Session.SetString("CurrentUserGroups", JsonSerializer.Serialize(groups));
        }
    }

    public void OnActionExecuted(ActionExecutedContext context){}
}

注册全局过滤器后,所有Action请求都会自动走这个校验逻辑,不会出现会话为空的问题。

额外注意事项

  • 你原有代码里的system.Security.Claims命名空间首字母需要大写,正确写法是System.Security.Claims
  • 尽量不要用ClaimsPrincipal.Current,异步场景下这个静态属性容易出现上下文丢失的问题,优先用当前HttpContext的User属性、或者事件参数中传递的Principal对象获取用户信息

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 12:39:24