Vuex處理用戶Token過(guò)期及優(yōu)化設(shè)置封裝本地存儲(chǔ)操作模塊
1. 處理用戶 Token

Token 是用戶登錄成功之后服務(wù)端返回的一個(gè)身份令牌,在項(xiàng)目中的多個(gè)業(yè)務(wù)中需要使用到:
- 訪問(wèn)需要授權(quán)的 API 接口
- 校驗(yàn)頁(yè)面的訪問(wèn)權(quán)限
- ...
問(wèn)題:Token往哪兒存?
我們只有在第一次用戶登錄成功之后才能拿到 Token。所以為了能在其它模塊中獲取到 Token 數(shù)據(jù),我們需要把它存儲(chǔ)到一個(gè)公共的位置,方便隨時(shí)取用。
本地存儲(chǔ)
- 獲取麻煩
- 數(shù)據(jù)不是響應(yīng)式
Vuex 容器(推薦)
- 獲取方便
- 響應(yīng)式的
使用容器存儲(chǔ) Token 的思路:

登錄成功,將 Token 存儲(chǔ)到 Vuex 容器中
- 獲取方便
- 響應(yīng)式
為了持久化,還需要把 Token 放到本地存儲(chǔ)
- 持久化
總結(jié): Vuex狀態(tài)管理工具可有可無(wú) (*?ω-q)
在 src/store/index.js 中
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
// 1. 存儲(chǔ)數(shù)據(jù)的地方 - 類(lèi)比于vue文件的data()
state: {
// 一個(gè)對(duì)象,儲(chǔ)存當(dāng)前登錄用戶的token數(shù)據(jù)
user: {}
},
// 2. 外界修改store中state的屬性值,必須通過(guò)mutations中設(shè)置的修改方法 - 類(lèi)比methods
// 注意:這里方法里面的代碼和.vue文件中的書(shū)寫(xiě)方式有差異,注意區(qū)分
mutations: {
setUser (state, data) {
state.user = data
}
},
// 3. 涉及到異步操作后修改state數(shù)據(jù)時(shí),必須先過(guò)actions中的自定義方法,通過(guò)actions去調(diào)用mutations中的方法
actions: {
},
// 4. 是state中數(shù)據(jù)的計(jì)算屬性 - 類(lèi)比computed
getters: {
},
// 5. 模塊化vuex,可以讓每一個(gè)模塊擁有自己的 state、mutation、action、 getters,使得結(jié)構(gòu)非常清晰,方便管理。
modules: {
}
})
登錄成功以后將后端返回的 token 調(diào)用commit方法存到store中
async onSubmit () {
...
try {
const res = await loginAPI(user)
console.log('登錄成功', res)
// 調(diào)用store中的方法,將接口返回的token存到狀態(tài)管理器中
this.$store.commit('setUser', res.data.data)
// 提示 success 或者 fail 的時(shí)候,會(huì)先把其它的 toast 先清除
this.$toast.success('登錄成功')
} catch (err) {
...
},

3. 將 store中的 token 相關(guān)數(shù)據(jù)存儲(chǔ)到容器中
const TOKEN_KEY = 'TOUTIAO_USER'
export default new Vuex.Store({
state: {
user: JSON.parse(window.localStorage.getItem(TOKEN_KEY))
},
mutations: {
setUser (state, data) {
state.user = data
// 為了防止刷新丟失,需要把數(shù)據(jù)備份到本地存儲(chǔ)
window.localStorage.setItem(TOKEN_KEY, JSON.stringify(state.user))
}
},
...
})
2. 優(yōu)化封裝本地存儲(chǔ)操作模塊 - 封裝localStrage功能
創(chuàng)建 src/utils/storage.js 模塊
- 存儲(chǔ)
- 獲取
- 刪除
// 封裝本地存儲(chǔ)操作模塊
/* 一個(gè)本地存儲(chǔ)的數(shù)據(jù)應(yīng)該擁有那些特性: 增刪改查 */
/*
儲(chǔ)存數(shù)據(jù) (新增, 修改)
*/
export const setItem = (key, value) => {
// 將數(shù)組,對(duì)象等引用數(shù)據(jù)類(lèi)型轉(zhuǎn)化為JSON字符串進(jìn)行存儲(chǔ)
// 將簡(jiǎn)單數(shù)據(jù)類(lèi)型直接存儲(chǔ)
// 需要外界使用該方法時(shí)傳入對(duì)一個(gè)的 鍵名
if (typeof value === 'object') {
// 將數(shù)組,對(duì)象等引用數(shù)據(jù)類(lèi)型轉(zhuǎn)化為JSON字符串進(jìn)行存儲(chǔ)
value = JSON.stringify(value)
}
window.localStorage.setItem(key, value)
}
/*
獲取數(shù)據(jù)
*/
export const getItem = key => {
// 如果該鍵存儲(chǔ)的是引用數(shù)據(jù)類(lèi)型的JSON字符串,那么需要進(jìn)行JSON.parse的轉(zhuǎn)化
const data = window.localStorage.getItem(key)
// 使用JSON.parse()做JSON數(shù)據(jù)轉(zhuǎn)化時(shí)可能會(huì)出現(xiàn)報(bào)錯(cuò)
// 1. 做條件判斷(要去找到所有滿足、不滿足的條件) 2. 做錯(cuò)誤判斷
try {
// 先嘗試做JSON.parse()的轉(zhuǎn)化,如果報(bào)錯(cuò)了,在把他當(dāng)成原始數(shù)據(jù)進(jìn)行返回
return JSON.parse(data)
} catch (error) {
return data
}
}
/*
刪除緩存數(shù)據(jù)
*/
export const removeItem = key => {
window.localStorage.removeItem(key)
}
在store/index.js引入方法
import { getItem, setItem } from '../utils/storage.js'
使用方法
import { getItem, setItem } from '../utils/storage.js'
const TOKEN_KEY = 'TOUTIAO_USER'
export default new Vuex.Store({
state: {
user:getItem(TOKEN_KEY)
},
mutations: {
setUser (state, data) {
state.user = data
// 為了防止刷新丟失,需要把數(shù)據(jù)備份到本地存儲(chǔ)
setItem(TOKEN_KEY, state.user)
}
},
...
})
3. Vuex各屬性的使用
創(chuàng)建測(cè)試用store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
// state存放狀態(tài),
state: {
name: 'tom', // 需要共用的數(shù)據(jù)
age: '22'
},
// getter為state的計(jì)算屬性
getters: {
getName: (state) => state.name, // 獲取name
getAge: (state) => state.age
},
// mutations可更改狀態(tài)的邏輯,同步操作
mutations: {
setName: (state, data) => { state.name = data },
setAge: (state, data) => { state.age = data }
},
// 提交mutation,異步操作
actions: {
acSetName (context, name) {
setTimeout(() => {
// 延時(shí)1秒提交至mutations中的方法
context.commit('setName', name)
}, 1000)
},
acSetAge (context, age) {
setTimeout(() => {
context.commit('setAge', age)
}, 1000)
}
},
// 將store模塊化
modules: {
}
})
創(chuàng)建頁(yè)面comOne.vue測(cè)試計(jì)算屬性
<template>
<div class="wrapper">
asd
<!-- 讀取mapGetters中的getName與getAge -->
<div>
name:<span>{{ getName }}</span>
</div>
<div>
age:<span>{{ getAge }}</span>
</div>
</div>
</template>
<script>
import { mapState, mapGetters } from 'vuex' // 導(dǎo)入vuex的輔助函數(shù)
export default {
components: {},
// 計(jì)算屬性computed無(wú)法傳遞參數(shù)
computed: {
// 映射 state 中的數(shù)據(jù)為計(jì)算屬性
...mapState(['name', 'age']),
// 映射 getters 中的數(shù)據(jù)為計(jì)算屬性
...mapGetters(['getName', 'getAge'])
}
}
</script>
<style scoped>
</style>
創(chuàng)建comTwo.vue測(cè)試同步異步方法
<template>
<div class="wrapper">
<div>
<span>同步修改:</span>
<!--直接回車(chē)調(diào)用mapMutations中的setName方法與setAge方法-->
<input
v-model="nameInp"
@keydown.enter="setName(nameInp)"
placeholder="同步修改name"
/>
<input
v-model="ageInp"
@keydown.enter="setAge(ageInp)"
placeholder="同步修改age"
/>
</div>
<div>
<span>異步修改:</span>
<!--直接回車(chē)調(diào)用mapAtions中的acSetName方法與acSetAge方法-->
<input
v-model="acNameInp"
@keydown.enter="acSetName(acNameInp)"
placeholder="異步修改name"
/>
<input
v-model="AcAgeInp"
@keydown.enter="acSetAge(AcAgeInp)"
placeholder="異步修改age"
/>
</div>
</div>
</template>
<script>
import { mapMutations, mapActions } from 'vuex' // 導(dǎo)入vuex的輔助函數(shù)
export default {
components: {},
data () {
return {
nameInp: '', // 綁定輸入框的值
ageInp: '',
acNameInp: '',
AcAgeInp: ''
}
},
methods: {
// 用于生成與 mutations 對(duì)話的方法,即:包含 $store.commit(xx) 的函數(shù)
...mapMutations(['setName', 'setAge']),
// 用于生成與 actions 對(duì)話的方法,即:包含 $store.dispatch(xx) 的函數(shù)
...mapActions(['acSetName', 'acSetAge'])
}
}
</script>
<style scoped>
</style>
4. 關(guān)于 Token 過(guò)期問(wèn)題
登錄成功之后后端會(huì)返回兩個(gè) Token:
token:訪問(wèn)令牌,有效期2小時(shí)refresh_token:刷新令牌,有效期14天,用于訪問(wèn)令牌過(guò)期之后重新獲取新的訪問(wèn)令牌
我們的項(xiàng)目接口中設(shè)定的 Token 有效期是 2 小時(shí),超過(guò)有效期服務(wù)端會(huì)返回 401 表示 Token 無(wú)效或過(guò)期了。
為什么過(guò)期時(shí)間這么短?
- 為了安全,例如 Token 被別人盜用
過(guò)期了怎么辦?
- 讓用戶重新登錄,用戶體驗(yàn)太差了
- 使用
refresh_token解決token過(guò)期
如何使用 refresh_token 解決 token 過(guò)期?
到課程的后面我們開(kāi)發(fā)的業(yè)務(wù)功能豐富起來(lái)之后,再給大家講解 Token 過(guò)期處理。
大家需要注意的是在學(xué)習(xí)測(cè)試的時(shí)候如果收到 401 響應(yīng)碼,請(qǐng)重新登錄。

5.優(yōu)化設(shè)置 Token
項(xiàng)目中的接口除了登錄之外大多數(shù)都需要提供 token 才有訪問(wèn)權(quán)限。
通過(guò)接口文檔可以看到,后端接口要求我們將 token 放到請(qǐng)求頭 Header 中并以下面的格式發(fā)送。

字段名稱:Authorization
字段值:Bearer token,注意 Bearer 和 token 之間有一個(gè)空格
方式一:在每次請(qǐng)求的時(shí)候手動(dòng)添加(麻煩)。
axios({
method: "",
url: "",
headers: {
Authorization: "Bearer token"
}
})
方式二:使用請(qǐng)求攔截器統(tǒng)一添加(推薦,更方便)。

在 src/utils/request.js 中添加攔截器統(tǒng)一設(shè)置 token:
import axios from 'axios'
import store from '../store/index.js'
const request = axios.create({
baseURL: 'http://toutiao.itheima.net/' // 接口的基準(zhǔn)路徑
})
// 請(qǐng)求攔截器
// Add a request interceptor
request.interceptors.request.use(function (config) {
// Do something before request is sent
// config :本次請(qǐng)求的配置對(duì)象
// config 里面有一個(gè)屬性:headers
const { user } = store.state
if (user && user.token) {
config.headers.Authorization = `Bearer ${user.token}`
}
return config
}, function (error) {
// 如果請(qǐng)求出錯(cuò) - 拋出異常
// Do something with request error
return Promise.reject(error)
})
api.user.js注釋掉store和獲取用戶信息攜帶的請(qǐng)求頭
import request from '@/utils/request'
// import store from '@/store'
/**
* 獲取用戶自己的信息
*/
export const getUserInfo = () => {
return request({
method: 'GET',
url: '/v1_0/user'
// 發(fā)送請(qǐng)求頭數(shù)據(jù)
// headers: {
// // 注意:該接口需要授權(quán)才能訪問(wèn)
// // token的數(shù)據(jù)格式:Bearer token數(shù)據(jù),注意 Bearer 后面有個(gè)空格
// Authorization: `Bearer ${store.state.user.token}`
// }
})
}以上就是Vuex處理用戶Token優(yōu)化設(shè)置封裝本地存儲(chǔ)操作模塊的詳細(xì)內(nèi)容,更多關(guān)于Vuex處理用戶Token的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue項(xiàng)目安裝less和less-loader的詳細(xì)步驟
這篇文章主要介紹了Vue項(xiàng)目安裝less和less-loader的詳細(xì)步驟,本文分步驟結(jié)合示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-12-12
vue使用路由的query配置項(xiàng)時(shí)清除地址欄的參數(shù)案例詳解
這篇文章主要介紹了vue使用路由的query配置項(xiàng)時(shí)如何清除地址欄的參數(shù),本文通過(guò)案例給大家分享完美解決方案,需要的朋友可以參考下2023-09-09
vue內(nèi)點(diǎn)擊url下載文件的最佳解決方案分享
這篇文章主要給大家介紹了關(guān)于vue內(nèi)點(diǎn)擊url下載文件的最佳解決方案,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-02-02
Vue兩個(gè)同級(jí)組件傳值實(shí)現(xiàn)
Vue組件之間是有聯(lián)系的,避免不了組件之間要互相傳值,那么如何實(shí)現(xiàn)Vue兩個(gè)同級(jí)組件傳值,本文就來(lái)介紹一下,感興趣的可以了解一下2021-07-07
Vue3 響應(yīng)式偵聽(tīng)與計(jì)算的實(shí)現(xiàn)
這篇文章主要介紹了Vue3 響應(yīng)式偵聽(tīng)與計(jì)算的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11

