在 Vue 2.5 實作「令牌自動刷新」功能

前言

後端使用 Laravel Passport 認證系統。

做法

在 Vuex 有一個 fetchToken() 方法。

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
actions: {
// 獲取令牌
fetchToken({
commit, rootState, rootGetters,
}, { params }) {
commit('setLoaded', false, { root: true });
return new Promise((resolve, reject) => {
axios({
method: 'POST',
url: '/auth/login',
data: params,
})
.then(({ data }) => {
// 判斷是否需要設置過期時間
const date = rootGetters.defaultKeep
? moment(parseInt(rootState.settings.createdAt, 10)).add(rootGetters.defaultKeepDays, 'd').toDate()
: null;
// 儲存令牌
cookie.set('payload', data, date);
commit('setPayload', cookie.get('payload'));
resolve(data);
})
.catch((error) => {
reject(error);
});
});
},

在元件有一個 getKeys() 方法,在執行 fetchKeys() 方法前,先執行 beforeProcess() 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 取得資源
async getKeys(args = null) {
await this.beforeProcess();
await this.fetchKeys({
params: {
q: this.query,
with: '',
page: this.page,
paginate: this.defaultPaginate,
},
args,
});
},

beforeProcess() 方法當中有 refreshToken() 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...mapActions('auth', [
'fetchToken',
]),
beforeProcess() {
return new Promise(async (resolve) => {
// 做一些其他事情
this.setError(null);
this.setNoData(false);
this.setLoading(true);
// 刷新令牌
await this.refreshToken();
resolve();
});
},

refreshToken() 方法判斷令牌是否過期,如果過期就刷新令牌。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
isExpended() {
// 計算令牌儲存後到當前所花費的時間
return moment.duration(moment().diff(this.payload.createdAt)).seconds();
},
isExpired() {
// 在令牌過期前 60 秒刷新
return this.payload && this.isExpended() > this.payload.data.expires_in - 60;
},
refreshToken() {
// 如果沒有過期則不刷新
if (!this.isExpired()) {
return false;
}
// 刷新令牌
return this.fetchToken({
params: {
grant_type: 'refresh_token',
client_id: process.env.VUE_APP_API_CLIENT_ID,
client_secret: process.env.VUE_APP_API_CLIENT_SECRET,
refresh_token: this.payload.data.refresh_token,
},
});
},