JWT令牌签名无效且过期时间不生效问题排查求助
解决JWT签名无效(Invalid signature)及过期时间校验失效问题
问题原因分析
签名无效
- 密钥长度不达标:HmacSha256签名算法要求密钥至少为128位(16个ASCII字符),如果
Constansts.JWT_SECURITY_KEY_FOR_TOKEN长度不足,会导致令牌生成与验证时的密钥字节数组不匹配,触发签名无效错误。 - 自定义策略绕过框架验证:
UserPolicy中直接调用ReadJwtToken解析令牌,完全跳过了JWT中间件的签名校验流程,即使令牌签名无效,仍会执行后续权限判断逻辑。
过期时间校验失效
- 未利用框架内置校验:AddJwtBearer配置的
ValidateLifetime=true未生效,因为自定义授权策略没有依赖框架的验证结果,而是手动解析令牌内容,绕开了过期时间校验。 - 时区差异问题:生成令牌时使用
DateTime.Now(本地时间),而JWT框架默认采用UTC时间进行过期校验,可能导致时间判断偏差。
解决方案
1. 修复签名无效问题
- 确保密钥符合算法要求:检查
Constansts.JWT_SECURITY_KEY_FOR_TOKEN,确保其长度至少为16个ASCII字符(例如"your-secure-secret-key-min-16-chars")。 - 依赖框架完成签名校验:自定义授权策略中不再手动解析令牌,而是通过
context.User获取已通过框架校验的Claims,只有通过JWT中间件验证的请求才会进入授权逻辑。
2. 修复过期时间校验失效问题
- 启用框架生命周期校验:移除自定义策略中手动解析令牌的代码,依赖AddJwtBearer配置的
ValidateLifetime=true和ClockSkew=TimeSpan.Zero自动完成过期时间校验。 - 生成令牌使用UTC时间:将令牌过期时间的生成逻辑从
DateTime.Now改为DateTime.UtcNow,避免时区差异导致的校验错误。
修改后的代码示例
调整JWT令牌生成管理器
// 将本地时间改为UTC时间生成过期时间 var tokenExpireTimeStamp = DateTime.UtcNow.AddMinutes(Constansts.JWT_TOKEN_EXPIRE_TIME);
修改自定义授权策略
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserPolicyRequirement requirement) { // 从已验证的context.User中获取用户ID,跳过手动解析令牌 var userIdClaim = context.User.FindFirst("userId"); if (userIdClaim == null) { context.Fail(); return Task.CompletedTask; } var request = _contextAccessor.HttpContext?.Request; string actionName = request.RouteValues["action"].ToString(); string controllerName = request.RouteValues["controller"].ToString(); var userId = userIdClaim.Value; if (dbContext.userAccessActions.Any(a => a.UserId == userId && a.ActionName == actionName && a.ControllName == controllerName)) { context.Succeed(requirement); } else { context.Fail(); } return Task.CompletedTask; }
补充JWT验证配置(可选,避免额外校验失败)
当前配置中ValidateAudience=true但未指定ValidAudience,如果生成令牌时也未设置Audience,会触发额外校验错误,建议补充配置:
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Constansts.JWT_SECURITY_KEY_FOR_TOKEN)), ValidateAudience = true, ValidAudience = "your-api-audience", // 例如你的API域名 ValidateIssuer = true, ValidIssuer = "your-auth-service", // 例如你的认证服务标识 ValidateLifetime = true, ClockSkew = TimeSpan.Zero, RequireExpirationTime = true, RequireSignedTokens = true };
同时在生成令牌的SecurityTokenDescriptor中添加对应配置:
var securityTokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(List), Expires = tokenExpireTimeStamp, Audience = "your-api-audience", Issuer = "your-auth-service", SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(tokenKey), SecurityAlgorithms.HmacSha256Signature) };
额外注意事项
- 确保
Constansts.JWT_SECURITY_KEY_FOR_TOKEN在令牌生成与验证环节完全一致,无空格、大小写差异。 - 生产环境必须开启
RequireHttpsMetadata=true,防止令牌被窃听。
内容的提问来源于stack exchange,提问作者sadegh
相关产品推荐
相关产品推荐

