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

ASP.NET Core集成Azure AD如何通过代码手动重定向登录页

问题根因

这个异常不是认证逻辑误判,属于ASP.NET Core认证的典型生产环境问题,核心诱因有两点:

  • 未配置Data Protection持久化密钥存储。默认情况下ASP.NET Core的数据保护密钥保存在应用进程内存中,应用崩溃重启、IIS应用池回收、多实例部署负载到不同节点时,内存中的密钥会被销毁,浏览器留存的旧认证Cookie里加密的用户身份声明、MS Graph访问令牌都无法被新启动的应用解密。
  • 缺少无效认证场景的兜底逻辑。无效Cookie解密失败后,认证中间件默认不会主动触发登录重定向,请求会带着空的身份信息进入标注了[Authorize]的控制器,直到调用Graph API时因为拿不到有效访问令牌抛出SDK异常。
    这个场景不是崩溃场景独有,属于生产环境必须兼容的常规情况,正常发版重启、容器漂移时都会触发。
解决方案

不要只靠全局异常捕获清Cookie,需要从根源配置+兜底逻辑两层处理。

1. 配置持久化数据保护密钥(必做生产配置)

这是解决问题的根本,配置后应用重启也能正常解密之前颁发的认证Cookie,不会出现身份失效问题。
在Program.cs中添加数据保护配置,根据部署场景选择持久化存储(文件系统、Redis、Key Vault、证书都可,以下是文件系统示例,生产环境建议用加密的集中存储):

// 提前创建指定目录,并授予应用运行账号读写权限
builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"C:\app-keys\myapp-dp-keys"))
    // 保持应用名固定,和之前颁发Cookie时的应用标识一致
    .SetApplicationName("YourAspNetCoreAppName");

如果是单实例部署到IIS、Azure App Service,平台默认会提供持久化密钥存储,但容器部署、自托管、多实例部署场景必须手动配置该项。

2. 无效认证场景兜底处理

针对已经生成的无法解密的旧Cookie、令牌过期/失效的场景,在认证管线早期做拦截,避免请求进入业务逻辑抛异常:
配置Cookie认证的校验事件,发现身份无效时直接清除Cookie,触发登录重定向:

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi(new[] { "User.Read" })
    .AddMicrosoftGraph(builder.Configuration.GetSection("Graph"))
    .AddInMemoryTokenCaches();

// 配置Cookie认证校验逻辑
builder.Services.Configure<CookieAuthenticationOptions>(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
    options.Events = new CookieAuthenticationEvents
    {
        OnValidatePrincipal = context =>
        {
            // 身份无效时直接拒绝,触发登录挑战
            if (context.Principal == null || !context.Principal.Identity.IsAuthenticated)
            {
                context.RejectPrincipal();
                // 清除所有无效Cookie
                foreach (var cookieKey in context.Request.Cookies.Keys)
                {
                    context.Response.Cookies.Delete(cookieKey);
                }
            }
            return Task.CompletedTask;
        }
    };
});

如果需要额外捕获业务代码中调用Graph的异常,可以再加一个全局异常过滤器做二次兜底:

public class AuthExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        // 捕获Graph服务401异常、令牌验证异常、Microsoft.Identity.Web抛出的无有效令牌异常
        var isAuthException = context.Exception switch
        {
            ServiceException se => se.StatusCode == HttpStatusCode.Unauthorized,
            SecurityTokenValidationException => true,
            InvalidOperationException ioe when ioe.Message.Contains("IDW10502") => true,
            _ => false
        };

        if (isAuthException)
        {
            foreach (var cookieKey in context.HttpContext.Request.Cookies.Keys)
            {
                context.HttpContext.Response.Cookies.Delete(cookieKey);
            }
            // 触发Azure AD登录重定向
            context.Result = new ChallengeResult(OpenIdConnectDefaults.AuthenticationScheme);
            context.ExceptionHandled = true;
        }
    }
}

把过滤器注册到全局配置即可生效:

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add<AuthExceptionFilter>();
});
补充注意事项
  • 数据保护持久化是使用ASP.NET Core Cookie认证的必配生产项,不配置的话除了重启场景,滚动更新、多实例部署时都会出现随机的登录失败、异常报错问题。
  • 调用下游API(包括MS Graph)时建议始终做异常捕获,即使配置完全正确,也可能出现用户权限变更、令牌过期、Graph服务临时不可用的情况,需要做友好的错误提示或者重试逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 14:27:18