.NET 6/7中如何动态返回401/403自定义认证响应?
.NET 6/7 动态返回401/403自定义响应实现方案
问题背景
在.NET 6/7开发中,已基于JWT实现401自定义响应,但需要根据实际错误场景动态返回401(未授权)和403(禁止访问)状态码,而非硬编码固定值。控制器方法使用[Authorize(Permissions.Master.Read)]属性做权限控制,需实现该动态响应需求。
现有JWT认证配置代码:
services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(o =>{ o.RequireHttpsMetadata = true; o.SaveToken = true; o.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, // ...其他配置 IssuerSigningKey = new SymmetricSecurityKey(key) }; });
当前自定义401的事件处理代码:
o.Events = new JwtBearerEvents { OnChallenge = async context => { // 跳过默认响应逻辑 var s = context.HttpContext.Response.StatusCode; // ***这里获取到的是200状态码*** context.HandleResponse(); var httpContext = context.HttpContext; var statusCode = StatusCodes.Status401Unauthorized; var routeData = httpContext.GetRouteData(); var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor()); var factory = httpContext.RequestServices.GetRequiredService<ProblemDetailsFactory>(); var problemDetails = factory.CreateProblemDetails(httpContext, statusCode); var result = new ObjectResult(problemDetails) { StatusCode = statusCode }; await result.ExecuteResultAsync(actionContext); } };
实现方案
401对应认证失败(无token、token无效/过期),403对应授权失败(token有效但无对应权限),需分别处理两种场景:
1. 优化认证挑战事件,支持动态状态码
修改JwtBearerEvents的OnChallenge方法,通过上下文信息区分认证/授权失败场景:
o.Events = new JwtBearerEvents { OnChallenge = async context => { context.HandleResponse(); var httpContext = context.HttpContext; int statusCode; string title; string detail; // 认证失败(无有效凭证)返回401 if (context.AuthenticateFailure != null) { statusCode = StatusCodes.Status401Unauthorized; title = "未授权"; detail = "请提供有效的身份凭证"; } // 授权失败(无对应权限)返回403 else if (httpContext.Items.ContainsKey("AuthorizationFailure")) { statusCode = StatusCodes.Status403Forbidden; title = "禁止访问"; detail = "您无此操作权限"; } // 默认 fallback else { statusCode = StatusCodes.Status401Unauthorized; title = "访问被拒绝"; detail = "身份验证未通过"; } var routeData = httpContext.GetRouteData(); var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor()); var factory = httpContext.RequestServices.GetRequiredService<ProblemDetailsFactory>(); var problemDetails = factory.CreateProblemDetails( httpContext, statusCode, title, detail: detail ); var result = new ObjectResult(problemDetails) { StatusCode = statusCode }; await result.ExecuteResultAsync(actionContext); }, // 可选:捕获token验证失败日志 OnAuthenticationFailed = context => { // 可添加日志记录逻辑 return Task.CompletedTask; } };
2. 自定义授权处理,标记授权失败状态
实现IAuthorizationMiddlewareResultHandler,在授权失败时向上下文添加标记,传递给认证挑战事件:
步骤1:定义自定义授权结果处理器
public class CustomAuthorizationResultHandler : IAuthorizationMiddlewareResultHandler { private readonly AuthorizationMiddlewareResultHandler _defaultHandler = new(); public async Task HandleAsync( RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult) { if (authorizeResult.Forbidden) { // 标记授权失败 context.Items["AuthorizationFailure"] = true; // 触发认证挑战,进入OnChallenge逻辑 await context.ChallengeAsync(JwtBearerDefaults.AuthenticationScheme); return; } // 其他场景使用默认处理逻辑 await _defaultHandler.HandleAsync(next, context, policy, authorizeResult); } }
步骤2:注册授权策略与处理器
在Program.cs中添加权限策略,并注册自定义处理器:
// 注册权限授权策略 services.AddAuthorization(options => { options.AddPolicy("Permissions", policy => policy.RequireAssertion(context => { // 从用户Claims中获取已拥有的权限 var userPermissions = context.User.Claims .Where(c => c.Type == "Permission") .Select(c => c.Value) .ToList(); // 获取当前请求要求的权限 var filterContext = context.Resource as AuthorizationFilterContext; var authorizeAttr = filterContext?.ActionDescriptor.EndpointMetadata .OfType<AuthorizeAttribute>() .FirstOrDefault(); if (authorizeAttr != null && !string.IsNullOrEmpty(authorizeAttr.Policy)) { // 匹配用户权限与要求的权限 return userPermissions.Contains(authorizeAttr.Policy); } return false; })); }); // 注册自定义授权结果处理器 services.AddSingleton<IAuthorizationMiddlewareResultHandler, CustomAuthorizationResultHandler>();
3. 控制器中使用权限授权属性
确保控制器方法的Authorize属性指定对应策略:
[Authorize(Policy = Permissions.Master.Read)] public IActionResult GetMasterData() { // 业务逻辑实现 return Ok(); }
效果验证
- 无token、token过期/无效时,触发认证失败,返回401自定义响应
- token有效但用户无对应权限时,触发授权失败,返回403自定义响应
内容的提问来源于stack exchange,提问作者Lajith
相关产品推荐
相关产品推荐

