Fork me on GitHub

koa 实现 token 有效时间续期的思路

# 场景描述

🍭 在前后端的交互中,无可避免的存在一些需要验证身份的请求。
🍭 一般来说,使用 token 作为身份令牌可以实现身份验证的问题。
🍭 以此同时,token 作为自带描述信息的无状态数据,唯一的判断标准就是生成 token 时设置的有效期时间,当超过有效期时则作废。
🍭 我们在使用 APPWEB 应用时,如果正在操作的时候,token 刚好过期了,那就出大问题了。
🍭 所有的数据请求都会失败,给用户带来极其糟糕的体验。
🍭 所以,如何才能让 token 进行续期呢?

# 处理思路

🍤 因为 token 是无状态数据,一旦生成了,不能主动删除或者让它失效,唯一的就是等待有效期时间到。
🍤 所以,我们会想到,在 token 过期时客户端携带新的 token 来访问数据接口,是不是就可以了呢。
🍤 答案是的,那么现在需要解决的问题就是:

1
2
1.怎么返回新的 token 给到客户端
2.什么时候返回 token 使得用户登录状态得到续期

# 思路

  1. 通过设置返回头设置新 token 的值,客户端使用 axios 进行响应拦截判断是否有新 token 字段,有则保存起来。
  2. 如果用户在一定的 token 有效时间段期间(比如有效期时间的后半段)访问了数据接口,就应该对 token 进行续期。

# 代码实现

🍥 本次项目采用的是 koa 基于 node 实现的 api 服务端。
🍥 主要文件有两个,一个是入口文件 app.js,另一个是工具函数文件 token.js
🍥 完整 github 项目可以查看 PPAP.server

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//token.js

const jwt = require('jsonwebtoken')
const secret = require('../config/config').secret

//判断token是否应该更新有效期(续期)
const getTokenRenewStatus = () => {

//检测当前token是否到达续期时间段
let obj = parseToken()
if(!obj.email){
return false
}
//更新时间段在过期前3天
if(obj.exp - new Date().getTime()/1000 > 60*60*24*3){
return false
}else{
return true
}

}

//获取一个期限为7天的token
const getToken = (payload = {}) => {
return jwt.sign(payload, secret, { expiresIn: 60*60*24*7 })
}

//重新生成一个期限为7天的token
const createNewToken = () => {

let token = global.token
let obj = jwt.verify(token, secret)
let payload = {
uid: obj.uid,
name: obj.name,
account: obj.account,
roleId: obj.roleId,
email: obj.email,
password: obj.password
}
return getToken(payload)

}

//解析token为对象
const parseToken = () => {

let token = global.token
try {
return jwt.verify(token, secret)
}catch {
console.log('token is expired')
return {}
}

}

module.exports = {
secret,
getTokenRenewStatus,
getToken,
createNewToken,
parseToken
}

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//app.js

const Koa = require('koa')
const bodyParser = require('koa-bodyparser')
const jwtKoa = require('koa-jwt') // 用于路由权限控制
const app = new Koa()

const config = require('./config/config')

const tokenUtil = require('./util/token')
const router = require('./router')

const jwtUnless = require('./util/jwt_unless') //用于判断是否需要jwt验证

//配置ctx.body解析中间件
app.use(bodyParser())

// 错误处理
app.use((ctx, next) => {
//设置CORS跨域
ctx.set("Access-Control-Allow-Origin", "*")
ctx.set("Access-Control-Allow-Methods", "OPTIONS, GET, PUT, POST, DELETE")
ctx.set("Access-Control-Allow-Headers", "x-requested-with, accept, origin, content-type, Authorization")
ctx.set("Content-Type", "application/json;charset=utf-8")
ctx.set("Access-Control-Expose-Headers", "new_token")
//获取token,保存全局变量
if(ctx.request.header.authorization){
global.token = ctx.request.header.authorization.split(' ')[1]
//检测当前token是否到达续期时间段
let obj = tokenUtil.parseToken()
//解析token携带的信息
global.uid = obj.uid
global.name = obj.name
global.account = obj.account
global.email = obj.email
global.roleId = obj.roleId
//先解析全局变量再执行next(),保证函数实时获取到变量值
}
return next().then(() => {
//执行完下面中间件后进入
//判断不需要jwt验证的接口,跳过token续期判断
if(jwtUnless.checkIsNonTokenApi(ctx)) return
//判断token是否应该续期(有效时间)
if(tokenUtil.getTokenRenewStatus()){
//设置header
ctx.set({
new_token: tokenUtil.createNewToken()
})
}
}).catch((err) => {
//携带token的Authorization参数错误
if(err.status === 401){
ctx.status = 200
ctx.body = {
status: 401,
message: '未携带token令牌或者token令牌已过期'
}
}else{
throw err
}
})
})

//配置不需要jwt验证的接口
app.use(jwtKoa({ secret: tokenUtil.secret }).unless({
//自定义过滤函数,详细参考koa-unless
custom: ctx => {
if(jwtUnless.checkIsNonTokenApi(ctx)){
//是不需要验证token的接口
return true
}else{
//是需要验证token的接口
return false
}
}
}));

//初始化路由中间件
app.use(router.routes()).use(router.allowedMethods())

//监听启动窗口
app.listen(config.port, () => console.log(`PPAP.server is run on ${config.host}:${config.port}`))
0%
            [00:06.59]呼んでいる 胸のどこか奥で
            [00:12.06]いつも心踊る 夢を見たい
            [00:18.09]かなしみは 数えきれないけれど
            [00:23.87]その向こうできっと あなたに会える
            [00:29.40]
            [00:31.81]繰り返すあやまちの そのたび ひとは
            [00:38.20]ただ青い空の 青さを知る
            [00:43.33]果てしなく 道は続いて見えるけれど
            [00:50.92]この両手は 光を抱ける
            [00:54.89]
            [00:57.26]さよならのときの 静かな胸
            [01:02.80]ゼロになるからだが 耳をすませる
            [01:08.60]生きている不思議 死んでいく不思議
            [01:14.53]花も風も街も みんなおなじ
            [01:20.50]
            [01:22.40]nananan lalala lululu
            [01:47.63]呼んでいる 胸のどこか奥で
            [01:53.56]いつも何度でも 夢を描こう
            [01:59.14]かなしみの数を 言い尽くすより
            [02:05.16]同じくちびるで そっとうたおう
            [02:11.48]
            [02:12.96]閉じていく思い出の そのなかにいつも
            [02:19.31]忘れたくない ささやきを聞く
            [02:24.47]こなごなに砕かれた 鏡の上にも
            [02:31.12]新しい景色が 映される
            [02:35.89]
            [02:38.27]はじまりの朝の静かな窓
            [02:43.72]ゼロになるからだ 充たされてゆけ
            [02:49.70]海の彼方には もう探さない
            [02:55.66]輝くものは いつもここに
            [03:01.66]わたしのなかに 見つけられたから
            [03:08.20]
            [03:14.13]nananan lalala lululu
        
            [by:嘶哑音符]
            [00:00.12]二人の間 通り過ぎた風は
            [00:07.15]どこから寂しさを運んできたの
            [00:13.66]泣いたりしたそのあとの空は
            [00:19.82]やけに透き通っていたりしたんだ
            [00:26.02]music
            [00:37.03]いつもは尖ってた父の言葉が
            [00:42.82]今日は暖かく感じました
            [00:48.47]優しさも笑顔も夢の語り方も
            [00:54.07]知らなくて全部 君を真似たよ
            [00:59.48]もう少しだけでいい あと少しだけでいい
            [01:05.39]もう少しだけでいいから
            [01:11.20]もう少しだけでいい あと少しだけでいい
            [01:16.97]もう少しだけ くっついていようか
            [01:21.11]music
            [01:25.69]僕らタイムフライヤー 時を駆け上がるクライマー
            [01:30.74]時のかくれんぼ はぐれっこはもういやなんだ
            [01:37.51]嬉しくて泣くのは 悲しくて笑うのは
            [01:41.74]君の心が 君を追い越したんだよ
            [01:46.78]music
            [02:08.22]星にまで願って 手にいれたオモチャも
            [02:14.10]部屋の隅っこに今 転がってる
            [02:19.83]叶えたい夢も 今日で100個できたよ
            [02:25.53]たった一つといつか 交換こしよう
            [02:31.26]music
            [02:37.14]いつもは喋らないあの子に今日は
            [02:42.95]放課後「また明日」と声をかけた
            [02:48.50]慣れないこともたまにならいいね
            [02:54.25]特にあなたが 隣にいたら
            [02:59.69]もう少しだけでいい あと少しだけでいい
            [03:05.39]もう少しだけでいいから
            [03:11.09]もう少しだけでいい あと少しだけでいい
            [03:16.69]もう少しだけくっついていようよ
            [03:21.02]music
            [03:25.75]僕らタイムフライヤー 君を知っていたんだ
            [03:30.42]僕が 僕の名前を 覚えるよりずっと前に
            [03:43.62]君のいない 世界にも 何かの意味はきっとあって
            [03:49.00]でも君のいない 世界など 夏休みのない 八月のよう
            [03:55.15]君のいない 世界など 笑うことない サンタのよう
            [04:00.60]君のいない 世界など
            [04:08.70]music
            [04:34.00]僕らタイムフライヤー 時を駆け上がるクライマー
            [04:39.32]時のかくれんぼ はぐれっこはもういやなんだ
            [04:46.03]なんでもないや やっぱりなんでもないや
            [04:50.46]今から行くよ
            [04:55.00]僕らタイムフライヤー 時を駆け上がるクライマー
            [04:59.98]時のかくれんぼ はぐれっこ はもういいよ
            [05:06.45]君は派手なクライヤー その涙 止めてみたいな
            [05:11.34]だけど 君は拒んだ 零れるままの涙を見てわかった
            [05:18.08]嬉しくて泣くのは 悲しくて 笑うのは
            [05:22.51]僕の心が 僕を追い越したんだよ
            [05:27.55]end
        
            [ti:Down by the salley gardens]
            [ar:藤田惠美]
            [al:挪威甘菊]
            [by:]
            [00:12.00]Down by the salley gardens my love and I did meet
            [00:25.00]She passed the salley gardens with little snow-white feet
            [00:38.00]She did me take love easy, as the leaves grow on the tree
            [00:50.00]But I, being young and foolish, with her would not agree
            [01:04.00]
            [01:15.00]In a filed by the river my love and I did stand
            [01:28.00]And on my leaning shoulder she laid her snows-white hand
            [01:40.00]She bid me take life easy, as the grass grows on the weirs
            [01:52.00]But I was young and foolish, and now I am full of tears
            [02:06.00]
            [02:54.00]Down by the salley gardens my love and I did meet
            [03:05.00]She passed the salley gardens with little snow-white feet
            [03:18.00]She did me take love easy, as the leaves grow on the tree
            [03:30.00]But I, being young and foolish, with her would not agree
        
            [00:11.860]夜明けまであと一時間 もうそろそろ行こう
            [00:23.120]聞こえるのは眠る君のかすかな寝息だけ
            [00:34.240]
            [00:37.710]目を閉じた君の横顔 とてもきれいだよ
            [00:49.440]さよなら 君の耳元にそっと囁いた
            [01:00.790]
            [01:01.480]ああ、僕は君を置いて 今ここを出て行く
            [01:12.970]外は雨 音もなく 僕の頬を濡らす
            [01:24.330]
            [01:36.410]君と出会ったのはたった半年前のこと
            [01:48.080]もうずいぶん前のことのような気がする
            [01:59.460]
            [01:59.900]今思えば僕らろくに話もしなかった
            [02:11.220]时间はいつも余るほどあったはずなのに
            [02:22.640]
            [02:23.090]ああ、僕は君を置いて 今ここを出て行く
            [02:34.740]外の雨は僕の涙、静かに降り続く
            [02:46.870]外の雨は僕の涙、静かに降り続く
            [02:59.480]
        
            [00:07.694]灯りを消したまま話を続けたら
            [00:19.901]ガラスの向こう側で星がひとつ消えた
            [00:26.559]からまわりしながら通りを駆け抜けて
            [00:37.816]砕けるその時は君の名前だけ呼ぶよ
            [00:45.374]広すぎる霊園のそばの このアパートは薄ぐもり
            [00:56.155]暖かい幻を見てた
            [01:04.441]猫になりたい 君の腕の中
            [01:13.254]寂しい夜が終わるまでここにいたいよ
            [01:22.621]猫になりたい 言葉ははかない
            [01:31.369]消えないようにキズつけてあげるよ
            [01:47.999]目を閉じて浮かべた密やかな逃げ場所は
            [01:57.880]シチリアの浜辺の絵ハガキとよく似てた
            [02:05.646]砂ぼこりにまみれて歩く 街は季節を嫌ってる
            [02:15.629]つくられた安らぎを捨てて
            [02:23.888]猫になりたい 君の腕の中
            [02:32.579]寂しい夜が終わるまでここにいたいよ
            [02:41.912]猫になりたい 言葉ははかない
            [02:50.362]消えないようにキズつけてあげるよ
        
            [by:最短距離]
            [00:08.40]落し物をしたのね そんな曇りの日に
            [00:15.78]プラネタリュウムに行って 重いドアを閉じましょうよ
            [00:23.28]いつのまに落としたの それさえわからなくて
            [00:30.55]涙をもう落とせない なら星見上げましょうよ
            [00:38.12]何がほんと作り事 答えは欲しくないけど
            [00:46.23]あなたが言う言葉は本当に したいよ
            [00:57.77]落し物をしたのね そんな曇りの日に
            [01:05.20]プラネタリュウムに行って 重いドアを閉じましょうよ
            [01:12.71]魔法信じれないけど 魔法みたいな力を
            [01:19.74]信じたくってこうして こんな星を見上げるんだ
            [01:26.89]差しわされた あなたの手を
            [01:30.27]私は今 ほら 握れなくて
            [01:34.12]通り過ぎてく 遠い答えを
            [01:37.72]言いたくても ほら言い出せない
            [01:57.90]
            [01:57.96]落し物をしたのね あなたを信じたことを
            [02:05.25]プラネタリュウム出るから 私のドア開けましょうよ
            [02:11.69]私のドア開けましょうよ
            [02:15.29]私のドア開けましょうよ
            [02:19.60]
            [02:19.67]はい!
            [02:21.60]はい!
            [02:23.47]はい!
            [02:24.89]はい!
            [02:26.91]はい!
            [02:28.74]はいはい!
            [02:30.97]はい!
            [02:32.40]はい!