JWT过期时间设置不生效:从”7d”到”7h”的深夜调试实录

2026年01月29日/ 浏览 8

正文:
凌晨三点的屏幕荧光刺得眼睛生疼,咖啡杯早已见底。我盯着控制台输出的JWT令牌,第37次尝试将过期时间从”7d”改成”7h”,可解码后的exp字段依然固执地显示七天后过期。这问题像一根卡在喉咙的鱼刺——看似简单,却让人寝食难安。


第一幕:迷雾中的配置项

项目采用Node.js+Express架构,JWT签发逻辑封装在authService.js中:
javascript
const token = jwt.sign(
{ userId: user.id },
process.env.JWT_SECRET,
{ expiresIn: '7h' } // 明明改成了7小时
);

环境变量通过dotenv加载,.env文件赫然写着:
ini
JWT_EXPIRES_IN=7h
JWT_SECRET=my_super_secret

甚至尝试硬编码:
javascript
{ expiresIn: '7h' } // 直接写死参数

但用https://jwt.io解码后,过期时间仍是604800秒(7天)。这见鬼的7d阴魂不散!


第二幕:依赖库的暗箭

排查进入死胡同时,突然想起项目中的config/index.js
javascript
module.exports = {
jwt: {
expiresIn: process.env.JWT_EXPIRES_IN || '7d' // 环境变量兜底7天?
}
};

但在代码中并未使用这个配置,难道是…?

执行console.log(process.env)时,发现一个诡异现象:
javascript
{
JWT_EXPIRES_IN: '7d', // 环境变量竟然是旧值?!
NODE_ENV: 'development'
}

原来团队引入了第三方配置库,其config/default.js中写着:
json
{
"jwt": {
"expiresIn": "7d"
}
}

致命发现:该库会自动将配置项转为环境变量,并以CONFIG_FORCE_为前缀强制覆盖!此时在终端执行:
bash
printenv | grep JWT

输出结果如五雷轰顶:
CONFIG_FORCE_jwt__expiresIn=7d
JWT_EXPIRES_IN=7h

环境变量优先级顺序:CONFIG_FORCE_ > 普通环境变量 > .env文件。旧配置像特洛伊木马般潜伏在系统中!


第三幕:版本差异的陷阱

解决环境变量冲突后,问题仍未消失。翻出package.json
json
"dependencies": {
"jsonwebtoken": "^8.5.1"
}

查源码时发现v8.5.1的签名函数有段玄机:
javascript
// jsonwebtoken/lib/sign.js
if (options.expiresIn) {
var exp = claim.iat + (options.expiresIn); // 直接相加?!
}

而新版v9.0.0已修复:
javascript
// 新版处理逻辑
if (typeof payload.exp !== 'number') {
payload.exp = iat + timespan(options.expiresIn);
}

原来旧版对expiresIn字符串(如”7h”)解析存在bug!紧急升级后:
bash
npm install jsonwebtoken@9.0.0


最终解决方案

组合拳出击
1. 清除环境变量污染
bash
unset CONFIG_FORCE_jwt__expiresIn

2. 升级依赖库
json
"jsonwebtoken": "^9.0.2"

3. 显式转换时间单位
javascript
// 添加容错处理
const expiresIn = typeof options.expiresIn === 'string' ?
ms(options.expiresIn) / 1000 : // 转为秒数
options.expiresIn;

4. 终极验证脚本
javascript
const token = jwt.sign({}, 'secret', { expiresIn: '7h' });
const decoded = jwt.decode(token, { complete: true });
console.log(decoded.header); // 检查alg等头部
console.log(new Date(decoded.payload.exp * 1000)); // 精确到毫秒的时间戳

(后记:两周后在生产环境发现另一个陷阱——某些JWT库要求expiresIn必须带单位后缀,纯数字会按毫秒解析!但那是另一个午夜惊魂的故事了…)

picture loss