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

如何触发自定义OWIN认证中间件的Challenge操作?

问题描述

Microsoft提供了大量库支持多种认证方式,包括使用Microsoft.Owin.Security.OpenIdConnect包的OpenID Connect。但该包在租户需不同配置的多租户场景中并不适用。

为解决此问题,我创建了一个“中间件路由器”——自定义中间件,手动调用带租户专属配置的OpenIdConnectAuthenticationMiddleware实例,代码如下:

public override Task Invoke(IOwinContext context)
{
    if (context.Authentication.User == null)
    {
        var options = new OpenIdConnectAuthenticationOptions();
        options.AuthenticationMode = AuthenticationMode.Passive;

        // configure options dynamically...

        var openIdConnectMiddleware = new OpenIdConnectAuthenticationMiddleware(Next, app, options);
        return openIdConnectMiddleware.Invoke(context);
    }

    return Next.Invoke(context);
}

但我无法显式触发这个新认证中间件的Challenge操作:

[AllowAnonymous]
public void OpenIdConnectLogin()
{
    if (!Request.IsAuthenticated)
    {
        HttpContext.GetOwinContext().Authentication.Challenge(
            new AuthenticationProperties { RedirectUri = "/" },
            OpenIdConnectAuthenticationDefaults.AuthenticationType);
    }
}

目前尚未弄清如何将AuthenticationType与自定义中间件关联,请问该如何触发自定义认证中间件的Challenge操作?

解决方案

问题核心在于:Owin的Authentication.Challenge是通过认证通知机制触发对应中间件的Challenge逻辑,而非直接走中间件的Invoke流程。你的自定义中间件目前只是在请求流程中动态创建OpenIdConnect中间件,但没有响应Challenge通知,所以无法被触发。

步骤1:给动态配置的OpenIdConnect设置专属AuthenticationType

每个租户的认证配置需要对应唯一的AuthenticationType,确保Challenge时能精准匹配。比如结合租户ID生成:

// 在自定义中间件中生成专属AuthenticationType
var tenantId = GetCurrentTenantId(context); // 自行实现获取当前租户ID的逻辑
var authType = $"OpenIdConnect_Tenant_{tenantId}";

var options = new OpenIdConnectAuthenticationOptions
{
    AuthenticationMode = AuthenticationMode.Passive,
    AuthenticationType = authType, // 设置专属认证类型
    // 其他动态配置:根据租户ID读取ClientId、Authority等
    ClientId = GetTenantConfig(tenantId).ClientId,
    Authority = GetTenantConfig(tenantId).Authority,
    // 其余OpenIdConnect配置...
};

步骤2:在自定义中间件中处理Challenge通知

修改自定义中间件的Invoke方法,先监听并处理Challenge请求,匹配到对应AuthenticationType时,调用OpenIdConnect中间件的Challenge逻辑:

public override Task Invoke(IOwinContext context)
{
    // 先处理Challenge通知
    var challenge = context.Authentication.AuthenticationResponseChallenge;
    if (challenge != null)
    {
        var tenantId = GetCurrentTenantId(context);
        var targetAuthType = $"OpenIdConnect_Tenant_{tenantId}";
        
        // 检查当前Challenge是否匹配租户专属认证类型
        if (challenge.AuthenticationTypes.Contains(targetAuthType))
        {
            var options = new OpenIdConnectAuthenticationOptions
            {
                AuthenticationMode = AuthenticationMode.Passive,
                AuthenticationType = targetAuthType,
                // 动态配置租户专属参数
                ClientId = GetTenantConfig(tenantId).ClientId,
                Authority = GetTenantConfig(tenantId).Authority,
                RedirectUri = "/" // 可根据需求调整,或从Challenge.Properties中获取
            };

            var openIdConnectMiddleware = new OpenIdConnectAuthenticationMiddleware(Next, App, options);
            // 调用OpenIdConnect中间件的Challenge处理逻辑
            return openIdConnectMiddleware.ApplyChallengeAsync(context, challenge.Properties);
        }
    }

    // 原有请求处理逻辑
    if (context.Authentication.User == null)
    {
        var tenantId = GetCurrentTenantId(context);
        var authType = $"OpenIdConnect_Tenant_{tenantId}";
        
        var options = new OpenIdConnectAuthenticationOptions
        {
            AuthenticationMode = AuthenticationMode.Passive,
            AuthenticationType = authType,
            // 动态配置租户参数...
            ClientId = GetTenantConfig(tenantId).ClientId,
            Authority = GetTenantConfig(tenantId).Authority
        };

        var openIdConnectMiddleware = new OpenIdConnectAuthenticationMiddleware(Next, App, options);
        return openIdConnectMiddleware.Invoke(context);
    }

    return Next.Invoke(context);
}

步骤3:在Controller中使用租户专属AuthenticationType触发Challenge

修改Login方法,根据当前租户生成对应的AuthenticationType:

[AllowAnonymous]
public void OpenIdConnectLogin()
{
    if (!Request.IsAuthenticated)
    {
        var tenantId = GetCurrentTenantId(HttpContext); // 自行实现获取租户ID逻辑
        var authType = $"OpenIdConnect_Tenant_{tenantId}";
        
        HttpContext.GetOwinContext().Authentication.Challenge(
            new AuthenticationProperties { RedirectUri = "/" },
            authType);
    }
}

补充说明

  • 确保GetCurrentTenantId方法能从请求中正确识别当前租户(比如通过域名、请求头、路由参数等)。
  • 如果需要支持多个租户同时触发Challenge,可调整逻辑遍历匹配所有可能的租户认证类型。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 18:12:29