如何检测Token是否过期

除了向服务器发起请求验证token是否过期以外,一些类型的token是直接将过期时间储存在token之中的,我们可以通过这一点在客户端本地验证token是否过期。

token的构成

token其实就是三个东西以小数点分隔开的字符串

例如:

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2V4YW1wbGUuYXV0aDAuY29tLyIsImF1ZCI6Imh0dHBzOi8vYXBpLmV4YW1wbGUuY29tL2NhbGFuZGFyL3YxLyIsInN1YiI6InVzcl8xMjMiLCJpYXQiOjE0NTg3ODU3OTYsImV4cCI6MTQ1ODg3MjE5Nn0.CA7eaHjIHz5NxeIJoFK9krqaeZrPLwmMmgI_XiQiIkQ

你可以在上面的token中发现两个小数点,而被这两个小数点分隔的部分分别是:

1
Header的base64.Payload的base64.Signature
  • Header中声明了token的元数据,例如使用的加密算法类型和token的类型
  • Payload中包含了实际的数据,例如用户名等,这部分的信息内容可由后端开发人员自由调整
  • Signature确保 JWT 未被篡改,如果 JWT 由服务器签发,服务器会用 secret 生成签名,客户端无法伪造

你其实可以随便在网上搜一个base64解码器,然后删除Signature部分进行解码,例如上述token删除Signature部分解码后的结果是:

1
{"alg":"HS256","typ":"JWT"}{"iss":"https://example.auth0.com/","aud":"https://api.example.com/calandar/v1/","sub":"usr_123","iat":1458785796,"exp":1458872196}

在本地检测token是否过期

在上述token拥有的数据中,payload部分的exp字段是一个时间戳,指定了该token的过期时间

时间戳就是从1970年1月1日至今以来的秒数或毫秒数,如果时间戳是10位,那么就是秒数,13位就是毫秒数,上面的例子中,使用的是秒级时间戳

通过exp字段,我们就能判断token什么时候过期,从而决定是否该使用refreshToken来刷新accessToken

我们可以实现一个方法来判断token是否过期(使用Dart实现,其他语言大同小异):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/// token过期或者格式错误则返回true,否则返回false
bool isTokenExpired(String token){
try{
final List<String> parts = token.split(".");
if(parts.length != 3){
throw Exception("Invalid Token");
}
// 对payload部分进行base64解码
final payloadString = utf8.decode(base64Url.decode(base64Url.normalize(parts[1])));
// 将解码出的字符串转化为Map对象
final Map<String, dynamic> payloadMap = json.decode(payloadString);
if(!payloadMap.containsKey("exp")){
throw Exception("Token doesn't have expiry date");
}
// 获取当前时间的秒级时间戳
final currentTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
// 与过期时间戳进行比较
if(currentTime < payloadMap['exp']){
return false;
}
else{
return true;
}
}
catch(e){
debugPrint("$e");
return true;
}
}