项目中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
相关产品推荐
相关产品推荐

