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

Azure B2C跨项目访问声明:实现方法及无法访问原因排查

问题分析与解决方案

首先,咱们先搞清楚为什么重定向到项目B后拿不到声明——核心原因是两个项目的认证上下文是完全隔离的,具体来说:

问题根源

  • Session隔离:你在项目A里把用户名存在Session["username"]里,但Session是基于每个应用独立的Cookie存储的,项目B无法读取项目A的Session Cookie,自然拿不到这个值。
  • 认证票据不共享:Owin的ClaimsPrincipal是存储在项目A的认证Cookie里的,这个Cookie是项目A专属的,项目B没有对应的认证信息,所以ClaimsPrincipal.Current在项目B里是空的。

接下来给你几种可行的解决方案,按推荐程度排序:

方案1:配置Azure AD B2C单点登录(SSO)——最推荐

这是最符合Azure B2C设计的方式,本质是让两个项目共享B2C的全局认证状态,步骤如下:

  1. 确保项目A和B都注册在同一个Azure AD B2C租户下(可以用不同的Client ID,但必须同租户)。
  2. 在项目B的Owin Startup类里,配置和项目A一致的Azure B2C OpenID Connect认证中间件。比如:
public void Configuration(IAppBuilder app)
{
    app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
    app.UseCookieAuthentication(new CookieAuthenticationOptions());

    app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
    {
        ClientId = "你的项目B Client ID",
        Authority = "https://your-tenant.b2clogin.com/tfp/your-tenant.onmicrosoft.com/B2C_1_你的用户流名称",
        RedirectUri = "项目B的回调地址(比如https://projectB.com/signin-oidc)",
        PostLogoutRedirectUri = "项目B的登出回调",
        Scope = "openid profile",
        ResponseType = "id_token",
        UseTokenLifetime = false, // 启用SSO,让认证状态跨应用保留
        Notifications = new OpenIdConnectAuthenticationNotifications
        {
            AuthenticationFailed = context =>
            {
                context.HandleResponse();
                context.Response.Redirect("/Error?message=" + context.Exception.Message);
                return Task.CompletedTask;
            }
        }
    });
}

配置完成后,当用户从项目A重定向到项目B时,项目B会自动检测用户已经在B2C中完成认证,自动获取并加载声明,不需要用户重新登录,你就能在项目B里正常访问ClaimsPrincipal.Current了。

方案2:传递并验证Id Token(适合跨域但无法用SSO的场景)

如果两个项目不在同一B2C租户,或者有特殊需求不能用SSO,可以把项目A拿到的Id Token传递给项目B,然后项目B验证令牌有效性后解析声明:

  1. 修改项目A的重定向逻辑,把Id Token作为参数传递(一定要用HTTPS,防止令牌泄露):
public void SignUpSignIn()
{
    if (!Request.IsAuthenticated)
    {
        HttpContext.GetOwinContext().Authentication.Challenge();
        return;
    }
    // 获取Id Token
    var idToken = ClaimsPrincipal.Current.FindFirst("id_token")?.Value;
    // 重定向到项目B并携带令牌
    Response.Redirect($"https://项目B地址/login?id_token={Uri.EscapeDataString(idToken)}");
}
  1. 在项目B的Login控制器里,验证令牌并创建认证上下文:
public ActionResult Login(string id_token)
{
    if (!string.IsNullOrEmpty(id_token))
    {
        try
        {
            var handler = new JwtSecurityTokenHandler();
            // 验证令牌的有效性:签名、issuer、audience、有效期等
            var validationParameters = new TokenValidationParameters
            {
                ValidIssuer = "https://your-tenant.b2clogin.com/your-tenant-id/v2.0/",
                ValidAudience = "项目A的Client ID", // 因为令牌是项目A获取的,受众是项目A的Client ID
                IssuerSigningKeys = GetB2CSigningKeys(), // 从B2C获取公钥来验证签名
                ValidateLifetime = true
            };

            ClaimsPrincipal principal;
            SecurityToken validatedToken;
            principal = handler.ValidateToken(id_token, validationParameters, out validatedToken);

            // 将验证后的用户信息设置到当前上下文
            HttpContext.GetOwinContext().Authentication.SignIn(principal.Identity);
            
            // 这里就能访问声明了
            var username = principal.FindFirst("signInName")?.Value;
            Session["username"] = username;
        }
        catch (Exception ex)
        {
            // 令牌验证失败,处理错误
            return Redirect("/Error?message=无效的身份令牌");
        }
    }
    return View();
}

// 辅助方法:获取B2C的签名公钥
private IEnumerable<SecurityKey> GetB2CSigningKeys()
{
    var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(
        "https://your-tenant.b2clogin.com/tfp/your-tenant.onmicrosoft.com/B2C_1_你的用户流名称/v2.0/.well-known/openid-configuration",
        new OpenIdConnectConfigurationRetriever());
    var config = configManager.GetConfigurationAsync().Result;
    return config.SigningKeys;
}

注意:令牌验证是必须的,不能直接信任传入的令牌,否则会有安全风险。

方案3:共享认证Cookie(仅限同域项目)

如果项目A和B在同一个根域名下(比如a.yourdomain.com和b.yourdomain.com),可以让两个项目共享同一个认证Cookie:

  1. 在项目A和B的CookieAuthentication配置里,设置相同的Cookie名称、根域名和加密密钥:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    CookieName = ".AspNet.SharedAuthCookie",
    CookieDomain = ".yourdomain.com", // 根域名,确保子域名能共享
    // 共享加密密钥,需要把密钥存储在两个项目都能访问的位置(比如共享文件夹)
    TicketDataFormat = new TicketDataFormat(
        new DataProtectorShim(
            DataProtectionProvider.Create(new DirectoryInfo(@"\\your-server\shared-keys"))
            .CreateProtector("Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware", "Cookie", "v2")))
});

这样两个项目就能读取同一个认证Cookie,项目B就能直接获取到ClaimsPrincipal和相关声明。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:49:16