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

项目中SignalR认证处理器与控制器CustomAuthorizeAttribute执行顺序问题求助

我来帮你分析下这个问题的根源,然后给出针对性的解决方案:

问题根源

你之前把"SignalR"设为了全局默认认证方案,这会导致所有HTTP请求(包括控制器接口)都会优先执行SignalRCustomAuthentication处理器。如果该处理器返回认证失败,请求管道会直接终止并返回401,根本没机会走到你的CustomAuthorizeAttribute(因为ActionFilter默认在认证步骤之后执行)。

要实现「只要访问控制器接口就执行CustomAuthorizeAttribute」的需求,核心是拆分SignalR和控制器的认证逻辑,让两者互不干扰。


解决方案步骤

1. 调整Startup认证配置,拆分认证方案

不再将SignalR认证设为全局默认,而是为SignalR单独指定认证方案,控制器使用独立的逻辑:

public void ConfigureServices(IServiceCollection services)
{
    // 注册认证服务,不设置全局默认方案
    services.AddAuthentication()
        // 添加SignalR专属认证方案
        .AddScheme<AuthenticationSchemeOptions, SignalRCustomAuthentication>("SignalR", null)
        // 可选:如果控制器需要JWT认证,添加控制器专属的JWT方案(根据你的需求调整)
        .AddJwtBearer("ControllerJwt", options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidIssuer = "你的Issuer",
                ValidAudience = "你的Audience",
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("你的密钥"))
            };
        });

    // 配置SignalR,指定使用"SignalR"认证方案
    services.AddSignalR(options =>
    {
        options.AuthorizationData.Add(new AuthorizeAttribute
        {
            AuthenticationSchemes = "SignalR"
        });
    });

    // 注册MVC服务,可选择全局添加CustomAuthorizeAttribute
    services.AddControllers(options =>
    {
        // 若要全局生效,取消注释下面一行
        // options.Filters.Add<CustomAuthorizeAttribute>();
    });
}

2. 优化CustomAuthorizeAttribute逻辑

你的CustomAuthorizeAttribute实现了IAsyncActionFilter,可以调整执行顺序并独立处理授权逻辑,不依赖全局认证结果:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CustomAuthorizeAttribute : Attribute, IAsyncActionFilter
{
    // 设置Order为最小值,确保在认证步骤之前执行(按需调整)
    public int Order { get; set; } = int.MinValue;

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        try
        {
            // 从Header获取Authorization Token
            if (!context.HttpContext.Request.Headers.TryGetValue("Authorization", out var tokenValue))
            {
                SetUnauthorizedResponse(context.HttpContext.Response);
                return;
            }

            string authHeaderToken = tokenValue.ToString();
            string[] tokenParts = authHeaderToken.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries);
            if (tokenParts.Length != 2 || !tokenParts[0].Equals("Bearer", StringComparison.OrdinalIgnoreCase))
            {
                // Token格式不符合要求
                SetUnauthorizedResponse(context.HttpContext.Response);
                return;
            }

            string accessToken = tokenParts[1];
            var jwtPrincipal = ValidatedToken(accessToken);
            if (jwtPrincipal == null)
            {
                // Token验证失败
                SetUnauthorizedResponse(context.HttpContext.Response);
                return;
            }

            // 验证通过,可将用户身份注入HttpContext(可选)
            context.HttpContext.User = jwtPrincipal;

            // 添加授权状态头
            context.HttpContext.Response.Headers.Add("AuthenticationStatus", Common.Utility.AUTHORIZED);
            // 继续执行后续Action和管道
            await next();
        }
        catch (Exception ex)
        {
            // 异常处理
            context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            context.HttpContext.Response.Headers.Add("AuthenticationStatus", "Error");
            return;
        }
    }

    private void SetUnauthorizedResponse(HttpResponse response)
    {
        response.Headers.Add("AuthenticationStatus", Common.Utility.UNAUTHORIZED_ACCESS);
        response.StatusCode = (int)HttpStatusCode.Unauthorized;
    }

    // 复用你原有的Token验证逻辑
    private ClaimsPrincipal ValidatedToken(string accessToken)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var validationParameters = new TokenValidationParameters
        {
            // 填入你的Token验证参数
        };
        try
        {
            ClaimsPrincipal principal = tokenHandler.ValidateToken(accessToken, validationParameters, out _);
            return principal;
        }
        catch
        {
            return null;
        }
    }
}

3. 在控制器上应用CustomAuthorizeAttribute

现在你可以在需要的控制器或Action上直接标记该属性:

[CustomAuthorize]
[ApiController]
[Route("api/[controller]")]
public class YourController : ControllerBase
{
    // 你的接口方法
}

效果说明

  • SignalR Hub会单独使用"SignalR"认证方案,执行SignalRCustomAuthentication处理器验证QueryString中的access_token。
  • 控制器接口会优先执行CustomAuthorizeAttribute,不管全局认证状态,完全按照你的需求触发逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 19:32:52