.NET 8微服务项目中JWT令牌验证失败导致心跳接口401 Unauthorized问题排查
首先看你给出的错误日志:IDX14100: JWT is not well formed, there are no dots (.),这个错误很明确——后端接收到的JWT格式不对,不是标准的三段式(Header.Payload.Signature)结构。结合你的描述,我帮你一步步排查:
第一步:确认前端实际发送的令牌是否正确
虽然你说localStorage.getItem("jwt")能拿到正确的令牌,但实际发送请求时可能出现意外。建议打开浏览器开发者工具的Network面板,找到POST /api/heartbeat的请求,查看Request Headers里的Authorization字段,确认是不是Bearer 后面跟着完整的、带两个点的JWT字符串。
常见的坑点:
- 拼接Authorization头时出错:比如多打了空格、或者令牌被意外转义(比如带了引号)。你可以把前端代码改成模板字符串的写法,更清晰不易出错:
async function sendHeartbeat() { const token = localStorage.getItem("jwt"); console.log("Token:", token); if (!token) return; try { await fetch("/api/heartbeat", { method: "POST", headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" // 可选,但如果接口需要,建议加上 } }); } catch (err) { console.error("Heartbeat failed:", err); } }
第二步:检查Nginx是否正确传递Authorization请求头
因为你用Nginx做反向代理,默认情况下Nginx可能不会传递一些自定义请求头,包括Authorization。你需要在Nginx的location配置块里显式添加:
location /api/heartbeat { proxy_pass http://your-hub-service-address; proxy_set_header Authorization $http_authorization; # 其他必要的代理配置,比如Host头 proxy_set_header Host $host; }
确保请求头完整传递到Hub服务,避免令牌被Nginx截断或丢失。
第三步:修正JWT验证配置的Issuer和Audience问题
虽然当前错误是格式问题,但即使格式修复后,你还会遇到新的验证失败——因为你生成JWT时没有指定Issuer和Audience,但Hub服务的验证配置里开启了ValidateIssuer = true和ValidateAudience = true,这会导致令牌因iss/aud不匹配被拒绝。
在Login服务中添加Issuer和Audience配置:
修改生成令牌的代码,从配置文件里读取Issuer和Audience:
var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new[] { new Claim(JwtRegisteredClaimNames.Name, loginRequest.Username) }), Expires = DateTime.UtcNow.AddHours(2), // 添加Issuer和Audience Issuer = config["Jwt:Issuer"], Audience = config["Jwt:Audience"], SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) };
在Hub服务中同步Issuer和Audience配置:
修改TokenValidationParameters,指定对应的有效值:
options.TokenValidationParameters = new TokenValidationParameters { IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)), ValidateIssuer = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], // 和Login服务一致 ValidateAudience = true, ValidAudience = builder.Configuration["Jwt:Audience"], // 和Login服务一致 ValidateLifetime = true, ValidateIssuerSigningKey = true, };
第四步:确认两端的Jwt:Key完全一致
最后要确保Login服务和Hub服务使用的Jwt:Key是完全相同的——包括大小写、空格、特殊字符,因为SymmetricSecurityKey是基于字节数组验证的,哪怕一个字符差异都会导致签名验证失败。
按照以上步骤排查后,应该能解决当前的401问题。如果还是有问题,可以再检查Hub服务的OnAuthenticationFailed事件日志,看是否有新的错误提示。
内容来源于stack exchange

