Asp.Net Core MVC 角色授权接口返回200而非401问题排查
Asp.Net Core MVC 默认的授权失败处理逻辑不会区分普通页面请求和API接口请求,只要校验不通过就会返回302重定向到登录页或AccessDenied拒绝访问页。而fetch默认会自动跟随重定向,最终拿到重定向后AccessDenied页面的200状态码,导致前端response.ok判断直接误判为请求成功。
你后续调整的靠JSON解析异常兜底的方案属于临时补丁,只要AccessDenied页面返回内容刚好是合法JSON,判断逻辑就会失效,必须从后端层面修正API请求的授权失败响应规则才能彻底解决。
1. 后端修改Cookie认证事件,针对API请求直接返回标准状态码
在Program.cs的服务注册阶段,修改Cookie认证的默认重定向逻辑,识别出API请求时直接返回对应状态码,不做重定向:
- 未登录身份校验不通过返回401 Unauthorized
- 已登录但角色/权限不足返回403 Forbidden
对应配置代码如下:
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { // 处理未登录的重定向逻辑 options.Events.OnRedirectToLogin = context => { // 识别API请求:路由带/api前缀、是AJAX请求、请求头声明接受JSON格式 bool isApiRequest = context.Request.Path.StartsWithSegments("/api") || context.Request.Headers["X-Requested-With"] == "XMLHttpRequest" || context.Request.Headers.Accept.Any(h => h?.Contains("application/json") == true); if (isApiRequest) { context.Response.StatusCode = StatusCodes.Status401Unauthorized; return Task.CompletedTask; } // 普通页面请求保持原有重定向逻辑 context.Response.Redirect(context.RedirectUri); return Task.CompletedTask; }; // 处理权限不足的重定向逻辑(即User角色访问Admin接口的场景) options.Events.OnRedirectToAccessDenied = context => { bool isApiRequest = context.Request.Path.StartsWithSegments("/api") || context.Request.Headers["X-Requested-With"] == "XMLHttpRequest" || context.Request.Headers.Accept.Any(h => h?.Contains("application/json") == true); if (isApiRequest) { context.Response.StatusCode = StatusCodes.Status403Forbidden; return Task.CompletedTask; } context.Response.Redirect(context.RedirectUri); return Task.CompletedTask; }; });
如果你的项目里所有API都统一使用/api路由前缀,只保留路径判断逻辑即可,不需要额外判断请求头。如果像当前场景一样把接口和页面混写在同一个控制器下,建议前端请求接口时统一带上Accept: application/json头,方便后端识别。
2. 简化接口代码
不需要特意给IsAdmin接口返回空对象,状态码逻辑修正后,原有接口代码即可正常工作:
[Authorize(Roles = "Admin")] public IActionResult IsAdmin() => Ok();
3. 调整前端请求逻辑
请求时主动带上JSON接受头,配合后端识别API请求,简化判断逻辑,不需要靠try-catch兜底:
async function isAdminAsync() { const response = await fetch('/Account/IsAdmin', { headers: { 'Accept': 'application/json' } }); // 只有200状态码才代表是管理员身份,401/403等异常状态直接返回false return response.ok; }
补充说明:如果你不想修改全局认证配置,也可以单独给接口加[ApiController]特性,Asp.Net Core 会自动给带该特性的控制器/接口处理授权状态码,不会触发重定向,但还是推荐全局配置的方式,后续新增接口不需要额外记加特性。
内容的提问来源于stack exchange,提问作者egeo

