移动端JWT令牌expiresAt时间不一致问题及expiresIn实现咨询
问题描述
我在APP中使用accessToken和refreshToken,已实现标准OAuth2令牌流程:
+--------+ +---------------+ | |--(A)------- Authorization Grant --------->| | | | | | | |<-(B)----------- Access Token -------------| | | | & Refresh Token | | | | | | | | +----------+ | | | |--(C)---- Access Token ---->| | | | | | | | | | | |<-(D)- Protected Resource --| Resource | | Authorization | | Client | | Server | | Server | | |--(E)---- Access Token ---->| | | | | | | | | | | |<-(F)- Invalid Token Error -| | | | | | +----------+ | | | | | | | |--(G)----------- Refresh Token ----------->| | | | | | | |<-(H)----------- Access Token -------------| | +--------+ & Optional Refresh Token +---------------+
当前服务器返回的令牌结构如下:
"token": "eyJhbGciOiJIUzI1NiI....", "refreshToken": "91792ee6-5cf4-45e6-96a5-4e5c47f4a601-188318b9-1591-41d7-9ba2-3d1b11f68383", "expiresAt": "2022-08-23T09:40:24Z"
现在客户端通过expiresAt结合设备时间判断accessToken是否过期,存在两个核心问题:
- 设备时间与服务器不一致时(比如实际时间14:00,设备显示13:30,令牌expiresAt为13:50,refreshToken过期时间13:51),客户端误判令牌有效,发起请求后服务器返回401错误
- 用户修改设备时间可让accessToken永不过期
想要改用expiresIn替代expiresAt,求具体实现方案。
实现方案
1. 服务器端调整令牌返回结构
将返回的expiresAt字段替换为expiresIn,字段值为accessToken的有效期时长(单位:秒),比如accessToken有效期为3600秒,则返回结构改为:
"token": "eyJhbGciOiJIUzI1NiI....", "refreshToken": "91792ee6-5cf4-45e6-96a5-4e5c47f4a601-188318b9-1591-41d7-9ba2-3d1b11f68383", "expiresIn": 3600
注意:如果是JWT令牌,本身payload里会包含
exp(过期时间戳)和iat(签发时间戳),可以直接从JWT中解析出expiresIn(exp - iat),服务器也可以额外返回该字段方便客户端直接使用。
2. 客户端核心逻辑实现
- 存储令牌时记录本地接收时间:当客户端收到服务器返回的令牌时,立即记录当前设备的本地时间戳(毫秒级),和
token、refreshToken、expiresIn一起存储。 - 判断令牌是否过期:每次发起请求前,计算当前本地时间戳与存储的接收时间戳的差值,若差值小于
expiresIn * 1000(转成毫秒),则认为令牌有效;反之则判定为过期,触发刷新流程。
示例伪代码:// 存储令牌时 const tokenData = { token: "eyJhbGciOiJIUzI1NiI....", refreshToken: "91792ee6-5cf4-45e6-96a5-4e5c47f4a601-188318b9-1591-41d7-9ba2-3d1b11f68383", expiresIn: 3600, receivedTimestamp: Date.now() // 本地接收时的时间戳(毫秒) }; saveToLocalStorage(tokenData); // 判断是否过期 const storedTokenData = getFromLocalStorage(); const currentTimestamp = Date.now(); const elapsedTime = currentTimestamp - storedTokenData.receivedTimestamp; const isTokenExpired = elapsedTime >= storedTokenData.expiresIn * 1000;
3. 异常情况处理
- 设备时间被修改:如果用户修改设备时间,客户端计算的
elapsedTime会出现偏差,但服务器端会严格基于自身时间校验令牌有效性,若客户端误判令牌有效发起请求,服务器会返回401,此时客户端需立即触发刷新流程。 - 刷新令牌过期:服务器返回刷新令牌时可附带
refreshExpiresIn字段,客户端用同样的方式(记录接收时间+计算差值)判断刷新令牌是否过期,若过期则直接引导用户重新登录。
4. 优化建议
- 提前刷新令牌:不要等令牌完全过期再刷新,比如在令牌剩余有效期的最后10%(如360秒)就发起刷新请求,避免因网络延迟导致请求失败。
- JWT令牌双重校验:如果使用JWT,客户端可以解析JWT的
exp字段,结合本地接收时间做双重校验,提升逻辑严谨性。
内容的提问来源于stack exchange,提问作者infinitesimal
相关产品推荐
相关产品推荐

