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

ASP.NET Core对接Azure AD实现SSO及混合身份认证的实现方案咨询

现有代码的安全风险

你当前的自定义回调实现存在多个高危安全漏洞,核心问题是完全绕过了OIDC协议的标准安全校验逻辑:

  • 没有校验id_token的数字签名,攻击者可以任意构造包含合法nonce的伪造令牌直接绕过校验
  • 缺少令牌核心声明校验:未验证颁发者iss是否为你的Azure AD租户、未验证受众aud是否为你的应用Client ID、未验证令牌过期时间exp,恶意租户的令牌、其他应用的令牌、过期令牌都能通过校验
  • 未校验OIDC流程中的state参数,存在CSRF攻击风险
  • 若你用内存存储_azureGuids,分布式部署时多实例会出现校验失败的问题,且没有过期清理逻辑会存在内存泄露风险
  • 若你当前使用隐式流返回id_token,该流程本身已被OIDC标准不推荐使用,令牌容易被劫持
正确实现方案

你的整体设计思路是合理的:Azure AD认证完成后兑换为原有系统的JWT,无需修改现有授权体系。不需要完全重写signin-oidc回调接口,Microsoft.Identity.Web预留了扩展点可以满足需求,所有标准安全校验由库内部完成,不需要自己实现:

方案1:使用OIDC事件回调(适合服务端渲染/混合架构)

直接在服务注册阶段配置OpenIdConnectOptions的事件钩子,认证通过后插入自定义逻辑生成自有JWT:

// Startup.cs/Program.cs 服务注册代码
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches();

// 配置OIDC事件
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
    // 令牌校验通过后触发,此时微软库已经完成了所有标准安全校验
    options.Events.OnTokenValidated = async context =>
    {
        // 从校验通过的身份信息中取Azure用户唯一标识
        var azureOid = context.Principal.FindFirst("oid")?.Value;
        // 匹配本地系统用户
        var localUser = await _userRepository.GetByAzureOidAsync(azureOid);
        if (localUser == null)
        {
            context.Fail("用户未在系统中注册");
            return;
        }
        // 生成原有体系的JWT
        var customJwt = _yourJwtService.GenerateToken(localUser);
        // 把自定义JWT带到重定向地址返回给前端
        context.Properties.RedirectUri = $"{context.Properties.RedirectUri}?custom_token={customJwt}";
    };
    // 如果需要自定义回调地址可以在这里修改,不需要自己写接口
    // options.CallbackPath = "/your-custom-signin-path";
});

方案2:前端MSAL认证后兑换令牌(适合前后端分离Web API场景)

更推荐前后端分离场景下,前端用MSAL.js完成Azure AD认证流程,拿到合法的Azure AD令牌后调用后端兑换接口获取自有JWT,后端只需要校验Azure令牌合法性即可:

[HttpPost("exchange-azure-token")]
[AllowAnonymous]
[RequireHttps]
public async Task<IActionResult> ExchangeToken([FromBody] string azureIdToken)
{
    var validationParams = new TokenValidationParameters
    {
        ValidIssuer = $"https://login.microsoftonline.com/{_azureAdConfig.TenantId}/v2.0",
        ValidAudience = _azureAdConfig.ClientId,
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        IssuerSigningKeyResolver = (token, securityToken, kid, _) =>
        {
            var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(
                $"https://login.microsoftonline.com/{_azureAdConfig.TenantId}/v2.0/.well-known/openid-configuration",
                new OpenIdConnectConfigurationRetriever());
            var openIdConfig = configManager.GetConfigurationAsync(CancellationToken.None).Result;
            return openIdConfig.SigningKeys.Where(k => k.Kid == kid);
        }
    };

    try
    {
        var handler = new JwtSecurityTokenHandler();
        var principal = handler.ValidateToken(azureIdToken, validationParams, out _);
        var azureOid = principal.FindFirst("oid")?.Value;
        var localUser = await _userRepository.GetByAzureOidAsync(azureOid);
        if (localUser == null) return Unauthorized();
        var customJwt = _yourJwtService.GenerateToken(localUser);
        return Ok(new { Token = customJwt });
    }
    catch (SecurityTokenValidationException)
    {
        return Unauthorized();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 18:06:05