ASP.NET JWT认证失败响应:从默认到自定义的优雅改造
本文主要介绍如何通过ASP.NET Core的JwtBearerEvents机制,实现JWT认证失败响应的深度定制。
1. 背景
在之前的文章《一个简单的ASP.NET一致性返回工具库》 中,我们介绍了 Sang.AspNetCore.CommonLibraries
这一通用库,它通过统一API响应模型和标准化的提示页面,显著提升了ASP.NET Core项目的开发效率。然而,当项目集成JWT(JSON Web Token)认证时,默认的授权失败响应(401/403状态码+www-authenticate
头)可能与团队约定的“业务状态码优先”规则产生冲突。
例如,某些团队要求所有接口必须返回HTTP 200状态码,并通过自定义的status
字段(如401表示未授权)标识业务状态。这种“200派”风格虽然违背了HTTP语义的纯粹性,却在某些前后端协作场景中广泛存在。本文将探讨如何利用ASP.NET Core的 JwtBearerEvents
机制,实现JWT认证失败响应的深度定制。
派别之争
2. 默认响应
ASP.NET Core的JWT认证模块严格遵循RFC 6750规范。当Token验证失败时,默认行为如下:
•401 Unauthorized:表示未提供有效Token(如未登录)。•403 Forbidden:表示Token有效但权限不足。•www-authenticate
头:携带错误类型(如error="invalid_token"
)和详情(如error_description
)。
content-length: 0
connection: close
date: Thu, 16 Jan 2025 09:38:23 GMT
server: Kestrel
www-authenticate: Bearer error="invalid_token", error_description="The token expired at '01/15/2025 21:18:12'"
无 AccessToken:
默认响应
Token验证失败:
默认响应
Token过期:
默认响应
这种设计对遵循HTTP标准的前端拦截器非常友好。例如,前端可通过检查状态码和www-authenticate
头获取更多的有效信息,或是根据状态码情况统一跳转登录页或提示权限不足。
无可否认,默认的已经足够优秀,但在特定场景下,我们可能需要对其进行定制化改造。比如与“200派”的冲突,若团队强制要求所有接口返回HTTP 200,并通过status
字段标识状态(如status=401
),默认的401/403响应会破坏这种约定。
3. 自定义响应:实战改造
这里我们需要使用的核心武器是 JwtBearerEvents
,ASP.NET Core的JWT认证模块提供了JwtBearerEvents
事件钩子,允许在以下场景中拦截请求并自定义响应:
•OnAuthenticationFailed:Token解析失败(如过期、签名错误)。•OnChallenge:请求未携带Token或Token无效。•OnForbidden:Token有效但权限不足。
以下示例展示如何通过事件处理,将所有认证失败响应格式化为HTTP 200 + JSON结构:
代码语言:javascript代码运行次数:0运行复制builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
// 省略TokenValidationParameters配置...
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
context.Response.StatusCode = 200;
context.Response.ContentType = "application/json";
return context.Response.WriteAsJsonAsync(new { status = 401, msg = "请重新登录" });
if (context.Exception is SecurityTokenExpiredException)
{
return context.Response.WriteAsJsonAsync(new { status = 401, msg = "Token 已过期" });
}
else if (context.Exception is SecurityTokenInvalidSignatureException)
{
return context.Response.WriteAsJsonAsync(new { status = 401, msg = "Token 签名无效" });
}
else
{
return context.Response.WriteAsJsonAsync(new { status = 401, msg = "Token 验证失败" });
}
},
OnChallenge = context =>
{
context.HandleResponse();
if (!context.Response.HasStarted)
{
context.Response.StatusCode = 200;
context.Response.ContentType = "application/json";
return context.Response.WriteAsJsonAsync(new { status = 401, msg = "未登录" });
}
return Task.CompletedTask;
},
OnForbidden = context =>
{
context.Response.StatusCode = 200;
context.Response.ContentType = "application/json";
return context.Response.WriteAsJsonAsync(new { status = 403, msg = "无权限访问" });
}
};
});
如果你想在token过期时返回跟多的信息,比如token的过期时间,可以在OnAuthenticationFailed
事件中的 content.Exception.Message
中获取更多的信息。
错误详情
4. 最后
在ASP.NET Core中,JWT认证的默认响应设计足够优秀,但在特定场景下,通过JwtBearerEvents
的灵活扩展,我们依然能实现“优雅的妥协”。无论是坚持HTTP语义的纯粹性,还是向业务现实低头,关键在于选择最适合团队协作和技术栈的方案。
发布评论