ASP.NET Core 2.0:REST API策略授权失败如何返回403而非200
这种情况我之前也碰到过——本质是授权失败时,框架默认会尝试触发默认挑战方案(也就是你的Cookie认证),导致API请求被重定向到登录页,最终客户端拿到200状态码和登录页内容。要解决这个问题,核心是让API请求在授权失败时跳过挑战流程,直接返回403。
下面分步骤给你具体实现方案:
1. 给API明确指定令牌认证方案
首先要确保API的控制器/Action只使用你的令牌认证方案,不会 fallback 到Cookie认证。
步骤1.1:配置认证时给令牌方案命名
在Startup.cs的ConfigureServices里,给你的自定义令牌认证Handler指定一个明确的方案名称(比如"Bearer"):
services.AddAuthentication(options => { // MVC默认用Cookie认证 options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddCookie(options => { options.LoginPath = "/Account/Login"; }) // 这里给你的自定义令牌认证方案起名"Bearer" .AddScheme<YourTokenAuthOptions, YourTokenAuthHandler>("Bearer", options => { });
步骤1.2:API控制器绑定令牌认证方案
在你的API控制器上,用[Authorize]属性指定只使用"Bearer"方案:
// 明确指定API用令牌认证方案 [Authorize(AuthenticationSchemes = "Bearer")] [ApiController] [Route("api/[controller]")] public class YourApiController : ControllerBase { // 你的API接口逻辑 }
如果有多个API策略,也可以创建专门的API授权策略,绑定认证方案:
services.AddAuthorization(options => { options.AddPolicy("ApiAccess", policy => { // 绑定令牌认证方案 policy.AuthenticationSchemes.Add("Bearer"); // 添加你的授权规则,比如角色、声明要求 policy.RequireRole("Admin"); policy.RequireAuthenticatedUser(); }); });
然后控制器用[Authorize(Policy = "ApiAccess")]即可。
2. 拦截授权失败,返回403响应
仅仅指定认证方案还不够,因为授权失败时框架还是会尝试触发挑战。我们需要自定义中间件,拦截API请求的授权失败场景,强制返回403。
实现自定义中间件
创建一个中间件类,在授权流程之后检查响应:
public class ApiAuthFailureMiddleware { private readonly RequestDelegate _next; public ApiAuthFailureMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { // 先执行后续中间件(包括授权逻辑) await _next(context); // 判断是否是API请求,且发生了授权失败导致的重定向 if (context.Request.Path.StartsWithSegments("/api") && context.Response.StatusCode == StatusCodes.Status302Found && context.User.Identity.IsAuthenticated) { // 清除重定向响应,替换为403 context.Response.Clear(); context.Response.StatusCode = StatusCodes.Status403Forbidden; await context.Response.WriteAsJsonAsync(new { message = "Access Denied: Insufficient permissions" }); } } }
注册中间件
在Startup.cs的Configure方法里,把这个中间件放在UseAuthorization之后、UseMvc之前:
app.UseAuthentication(); app.UseAuthorization(); // 注册自定义中间件 app.UseMiddleware<ApiAuthFailureMiddleware>(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); });
3. 可选:自定义授权Handler(更精准控制)
如果需要更细粒度的授权逻辑控制,可以实现IAuthorizationHandler,在授权失败时直接设置403响应:
自定义授权Handler
public class ApiRequirementHandler : AuthorizationHandler<ApiAccessRequirement> { private readonly IHttpContextAccessor _httpContextAccessor; public ApiRequirementHandler(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ApiAccessRequirement requirement) { var httpContext = _httpContextAccessor.HttpContext; // 检查用户是否满足授权要求 if (!context.User.HasClaim(c => c.Type == "Permission" && c.Value == "AccessApi")) { // 授权失败,直接设置403响应 httpContext.Response.StatusCode = StatusCodes.Status403Forbidden; context.Fail(); return Task.CompletedTask; } context.Succeed(requirement); return Task.CompletedTask; } } // 定义自定义授权要求 public class ApiAccessRequirement : IAuthorizationRequirement { }
注册Handler和策略
在Startup.cs的ConfigureServices里:
services.AddScoped<IAuthorizationHandler, ApiRequirementHandler>(); services.AddAuthorization(options => { options.AddPolicy("ApiAccess", policy => { policy.AuthenticationSchemes.Add("Bearer"); policy.RequireAuthenticatedUser(); policy.AddRequirements(new ApiAccessRequirement()); }); });
这样,当API请求触发授权失败时,会直接返回403,不会再触发Cookie认证的挑战重定向。
内容的提问来源于stack exchange,提问作者LetMeSleepAlready

