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

.NET 6 Ocelot网关自定义认证处理器:兼容SSO JWT与专属ID认证

问题:在Ocelot网关中实现多方式认证(Azure AD SSO + 专属ID)

我有一个基于.NET 6.0.0(使用Ocelot)的网关API,需要支持两种认证方式:

  • Azure AD生成的SSO JWT令牌认证
  • 应用生态专属ID认证

但Ocelot每个端点仅允许指定一个AuthenticationProviderKey,所以我打算写一个自定义认证处理器,根据请求中的logintype头自动选择认证逻辑。现在卡在如何把原有的Azure AD SSO认证逻辑复用到自定义处理器里——原逻辑完全依赖Microsoft提供的Authority、MetadataAddress等配置,不知道怎么在自定义处理器里复用这套外部令牌验证逻辑。


原Azure AD认证配置代码

var authenticationProviderKey = "AzureAdJwtBearer";
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(authenticationProviderKey, opt =>
    {
        opt.Audience = config.GetValue<string>("AzureAd:Audience");
        opt.Authority = $"address1" + config.GetValue<string>("AzureAd:TenantId");
        opt.MetadataAddress = $"address2" + config.GetValue<string>("AzureAd:TenantId") + "/v2.0/.well-known/openid-configuration";
    });

我尝试的未完成实现

Program.cs 修改后的代码

builder.Services.AddAuthentication(options => options.DefaultScheme = TwoAuthSchemeOptions.Name)
    .AddScheme<TwoAuthsSchemeOptions, TwoAuthsHandler>(TwoAuthsSchemeOptions.Name, options => { });

TwoAuthsSchemeOptions.cs

public class TwoAuthsSchemeOptions : AuthenticationSchemeOptions
{
    public const string Name = "TwoAuthsScheme";
}

TwoAuthsHandler.cs(未完成)

public class TwoAuthsHandler : AuthenticationHandler<TwoAuthsSchemeOptions>
{
    public TwoAuthsHandler(IOptionsMonitor<TwoAuthsSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
    {
    }

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var loginTypeHeader = Request.Headers["logintype"].ToString();

        switch (loginTypeHeader)
        {
            case "sso":
                try
                {
                    // 这里需要复用原有的Azure AD SSO认证逻辑
                    return Task.FromResult(AuthenticateResult.Success(ticket));
                }
                catch(Exception ex)
                {
                    return Task.FromResult(AuthenticateResult.Fail("User authentication failed or session expired"));
                }
            case "special":
                try
                {
                    // 专属ID认证逻辑已实现
                    return Task.FromResult(AuthenticateResult.Success(ticket));
                }
                catch
                {
                    return Task.FromResult(AuthenticateResult.Fail("User authentication failed or session expired"));
                }
            default:
                return Task.FromResult(AuthenticateResult.Fail("User authentication failed or session expired"));
        } 
    }
}

遇到的困境

我不能直接用硬编码的TokenValidationParameters来验证令牌(如下方代码),因为Azure AD的令牌是外部生成的,原认证逻辑完全依赖Authority和MetadataAddress来自动获取签名密钥、颁发者等信息,不知道怎么在自定义处理器里实现这套逻辑。

tokenHandler.ValidateToken(token, new TokenValidationParameters
{
    ValidateIssuerSigningKey = true,
    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("secret")),
    ValidateIssuer = true,
    ValidIssuer = "someissuer",
    ValidAudience = "someaudience",
    ValidateAudience = true,
    ClockSkew = TimeSpan.Zero
}, out SecurityToken securityToken);

解决方案

核心思路是在自定义处理器中复用已注册的JwtBearer认证服务,而不是自己重新实现Azure AD的令牌验证逻辑。

步骤1:保留原Azure AD JwtBearer配置

不要移除原有的AddJwtBearer配置,同时注册自定义认证方案:

var authenticationProviderKey = "AzureAdJwtBearer";
// 保留原Azure AD JWT认证配置
builder.Services.AddAuthentication(options => 
{
    options.DefaultScheme = TwoAuthsSchemeOptions.Name;
})
.AddJwtBearer(authenticationProviderKey, opt =>
{
    opt.Audience = config.GetValue<string>("AzureAd:Audience");
    opt.Authority = $"address1" + config.GetValue<string>("AzureAd:TenantId");
    opt.MetadataAddress = $"address2" + config.GetValue<string>("AzureAd:TenantId") + "/v2.0/.well-known/openid-configuration";
})
// 注册自定义认证方案
.AddScheme<TwoAuthsSchemeOptions, TwoAuthsHandler>(TwoAuthsSchemeOptions.Name, options => { });

步骤2:修改自定义处理器,注入JwtBearerHandler

在TwoAuthsHandler中注入IAuthenticationHandlerProvider和IAuthenticationSchemeProvider,用来获取已注册的Azure AD JwtBearer处理器,直接调用它的认证逻辑:

public class TwoAuthsHandler : AuthenticationHandler<TwoAuthsSchemeOptions>
{
    private readonly IAuthenticationHandlerProvider _handlerProvider;
    private readonly IAuthenticationSchemeProvider _schemeProvider;

    public TwoAuthsHandler(
        IOptionsMonitor<TwoAuthsSchemeOptions> options, 
        ILoggerFactory logger, 
        UrlEncoder encoder, 
        ISystemClock clock,
        IAuthenticationHandlerProvider handlerProvider,
        IAuthenticationSchemeProvider schemeProvider) : base(options, logger, encoder, clock)
    {
        _handlerProvider = handlerProvider;
        _schemeProvider = schemeProvider;
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var loginTypeHeader = Request.Headers["logintype"].ToString();

        switch (loginTypeHeader)
        {
            case "sso":
                // 获取Azure AD JwtBearer的认证方案
                var azureAdScheme = await _schemeProvider.GetSchemeAsync("AzureAdJwtBearer");
                if (azureAdScheme == null)
                {
                    return AuthenticateResult.Fail("Azure AD authentication scheme not found");
                }
                // 获取对应的认证处理器
                var jwtHandler = await _handlerProvider.GetHandlerAsync(Context, azureAdScheme.Name) as JwtBearerHandler;
                if (jwtHandler == null)
                {
                    return AuthenticateResult.Fail("Failed to get Azure AD authentication handler");
                }
                // 直接调用JwtBearer的认证逻辑
                var ssoResult = await jwtHandler.AuthenticateAsync();
                return ssoResult.Succeeded ? ssoResult : AuthenticateResult.Fail("SSO authentication failed: " + ssoResult.Failure.Message);
            
            case "special":
                // 执行专属ID认证逻辑
                var specialId = Request.Headers["special-id"].ToString();
                if (string.IsNullOrEmpty(specialId) || !ValidateSpecialId(specialId))
                {
                    return AuthenticateResult.Fail("Invalid special ID");
                }
                var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.NameIdentifier, specialId),
                    new Claim("logintype", "special")
                };
                var identity = new ClaimsIdentity(claims, TwoAuthsSchemeOptions.Name);
                var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), TwoAuthsSchemeOptions.Name);
                return AuthenticateResult.Success(ticket);
            
            default:
                return AuthenticateResult.Fail("Invalid login type");
        } 
    }

    // 替换成实际的专属ID验证逻辑
    private bool ValidateSpecialId(string specialId)
    {
        return specialId.StartsWith("APP_") && specialId.Length == 16;
    }
}

步骤3:Ocelot配置中指定自定义认证方案

在Ocelot的路由配置里,将AuthenticationProviderKey设置为自定义方案的名称TwoAuthsScheme:

"Routes": [
    {
        "DownstreamPathTemplate": "/api/{everything}",
        "DownstreamScheme": "https",
        "DownstreamHostAndPorts": [
            {
                "Host": "your-backend-service",
                "Port": 443
            }
        ],
        "UpstreamPathTemplate": "/api/{everything}",
        "AuthenticationOptions": {
            "AuthenticationProviderKey": "TwoAuthsScheme",
            "AllowedScopes": []
        }
    }
]

关键说明

  1. 复用官方JwtBearer逻辑:通过IAuthenticationHandlerProvider获取已配置好的JwtBearerHandler,直接调用其AuthenticateAsync方法,完全复用Azure AD的令牌验证逻辑(包括自动从Metadata地址获取签名密钥、验证颁发者/受众等)。
  2. 避免重复配置:原有的AddJwtBearer配置不需要修改,确保所有Azure AD的验证参数都保持一致。
  3. 灵活扩展:如果以后需要增加其他认证方式,只需要在switch分支中添加对应的逻辑即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 15:05:20