将应用从.NET Core 2.2升级至3.1后出现JwtSecurityToken过期时间无效引发401未授权错误
解决.NET Core 3.1升级后JWT令牌未过期却返回401的问题
我一眼就揪出了问题的核心——你在生成JWT令牌时,把nbf(生效时间)和exp(过期时间)这两个关键声明存成了字符串类型,而.NET Core 3.1的JWT验证器比2.2版本更严格遵循JWT标准,要求这两个声明必须是数值类型(NumericDate,即Unix时间戳整数),所以验证时直接判定令牌无效,返回401。
问题根源代码
看你生成令牌的这段代码:
Claim claimEffDate = new Claim(JwtRegisteredClaimNames.Nbf, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString()); lc.Add(claimEffDate); Claim claimExpDate = new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(DateTime.Now.AddDays(tokenLifespan)).ToUnixTimeSeconds().ToString()); lc.Add(claimExpDate);
这里你把ToUnixTimeSeconds()返回的整数转成了字符串存入Claim,而JWT标准明确要求nbf和exp是数字格式,.NET Core 3.1的验证逻辑不再自动兼容字符串格式的数值声明。
两种修复方案
方案1:直接使用数值类型的Claim
去掉字符串转换,明确指定Claim的数值类型:
// 不转字符串,直接传入整数并指定类型为Integer64 Claim claimEffDate = new Claim(JwtRegisteredClaimNames.Nbf, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Integer64); lc.Add(claimEffDate); Claim claimExpDate = new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(DateTime.Now.AddDays(tokenLifespan)).ToUnixTimeSeconds().ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Integer64); lc.Add(claimExpDate);
通过ClaimValueTypes.Integer64确保声明被验证器正确识别为数值类型。
方案2:使用JwtPayload属性直接设置(更推荐)
直接通过JwtPayload的内置属性设置生效和过期时间,无需手动添加Claim,更不易出错:
// 替换原有的JwtPayload构造逻辑 var payload = new JwtPayload { { ClaimTypes.Name, sportapikey.Client } }; // 直接设置生效和过期时间 payload.NotBefore = new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds(); payload.Expires = new DateTimeOffset(DateTime.Now.AddDays(tokenLifespan)).ToUnixTimeSeconds(); // 添加其他自定义声明 foreach (string team in sportapikey.Teams) { payload.Add(ClaimTypes.System, team.Trim()); } foreach (string sport in sportapikey.Sports.Split(",")) { payload.Add(ClaimTypes.Role, sport.Trim()); } // 生成令牌 token = new JwtSecurityToken( new JwtHeader(new SigningCredentials( new SymmetricSecurityKey(Encoding.UTF8.GetBytes("AnythingYouWant")), SecurityAlgorithms.HmacSha256)), payload);
验证修复效果
修复后重新生成令牌,用jwt.io解码查看nbf和exp字段,它们应该显示为无引号的数字而非字符串,此时再用Postman测试接口,401未授权错误应该就会消失。
另外提个小建议:你在Startup里的UseCors()最好配置具体的跨域规则,默认允许所有的设置在生产环境存在安全风险。
内容的提问来源于stack exchange,提问作者Bmoe




