asp.net core token认证需严格遵循中间件顺序:userouting()→useauthentication()→useauthorization()→useendpoints(),且jwt必须配置tokenvalidationparameters、authorization头格式为“bearer+空格+token”、token载荷含sub声明,否则httpcontext.user为空。

ASP.NET Core 中的 Token Based 身份验证不是加个 AddJwtBearer() 就能跑通——顺序错、声明漏、头传错,HttpContext.User 就是空的,[Authorize] 也不报错,静默放行或一律 401。
UseAuthentication 和 UseAuthorization 必须严格按序注册
中间件注册顺序直接影响整个认证链是否生效。一旦错位,HttpContext.User 始终为 null,所有授权逻辑形同虚设。
-
UseRouting()必须在最前(它解析端点,为后续中间件提供上下文) -
UseAuthentication()紧跟其后,负责从请求中提取并验证凭证 -
UseAuthorization()必须在UseAuthentication()之后、UseEndpoints()之前,否则策略无法读取已认证的User -
UseEndpoints()放最后,承载路由和控制器分发
常见错误:把 UseAuthentication() 写在 UseRouting() 之前,或插在 UseEndpoints() 里面——这两种写法都会导致 User.Identity.IsAuthenticated 恒为 false。
JWT 验证必须显式配置 TokenValidationParameters
AddAuthentication().AddJwtBearer() 只是注册了处理逻辑,真正决定“验什么”“怎么验”的是 TokenValidationParameters。缺配置 ≠ 默认跳过校验,而是默认全部开启且校验失败。
-
ValidateIssuer和ValidateAudience设为true时,ValidIssuer/ValidAudience不能为空字符串或null;若想跳过 issuer 校验,得设为string.Empty -
IssuerSigningKey必须是SymmetricSecurityKey实例,不能直接传 Base64 字符串;要先用Convert.FromBase64String()解码再构造 -
ValidateLifetime开启后,exp和iat都会被检查;调试时可临时加clockSkew = TimeSpan.Zero排除时间偏移干扰
示例关键片段:
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidateAudience = true,
ValidAudience = builder.Configuration["Jwt:Audience"],
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
};
Authorization header 格式必须严格为 Bearer + 空格 + Token
客户端传错格式,JwtBearerHandler 会直接忽略该请求,不触发任何验证逻辑,也不抛异常——这是最隐蔽的“401 不报错”原因。
- ✅ 正确:
Authorization: Bearer eyJhbGciOi...(注意Bearer后有一个英文空格) - ❌ 错误:
Authorization: BearereyJhbGciOi...(无空格)、Authorization: bearer eyJhbGciOi...(小写 scheme)、Authorization: Basic xxx(用错 scheme) - IIS 或 Nginx 反向代理默认剥离
Authorization头,需额外配置透传:Nginx 加proxy_set_header Authorization $http_authorization;,IIS 在web.config中启用forwardWindowsAuthToken="false"并确保Authorization不被过滤
Token 必须含 sub 声明,否则 IsAuthenticated 恒为 false
JWT 默认只将 name claim 映射为 Identity.Name,但 User.Identity.IsAuthenticated 的判定逻辑依赖 sub(subject)是否存在且非空。签发时漏掉 sub,哪怕签名、过期时间、issuer 全对,用户也始终是未认证状态。
- 签发 Token 时务必显式添加
sub,推荐值为用户唯一标识(如数据库 ID 或用户名) - 使用
JwtSecurityToken构造时,不要只传 header/payload/sig,而漏掉claims参数列表;建议用new JwtPayload(...)或new ClaimsIdentity(...)统一管理 - 调试时可用 jwt.io 粘贴 Token 查看载荷,确认
sub字段存在且值合法;也可在OnTokenValidated回调里加断点检查context.Principal.Identity
最容易被忽略的是:sub 是 JWT 规范强制要求的注册声明,.NET 的 ClaimsPrincipal 实现把它当作认证成功的硬性依据——这不是可配选项,是底层行为。











