Vuex處理用戶Token過期及優(yōu)化設(shè)置封裝本地存儲操作模塊
1. 處理用戶 Token
Token 是用戶登錄成功之后服務(wù)端返回的一個身份令牌,在項目中的多個業(yè)務(wù)中需要使用到:
- 訪問需要授權(quán)的 API 接口
- 校驗頁面的訪問權(quán)限
- ...
問題:Token往哪兒存?
我們只有在第一次用戶登錄成功之后才能拿到 Token。所以為了能在其它模塊中獲取到 Token 數(shù)據(jù),我們需要把它存儲到一個公共的位置,方便隨時取用。
本地存儲
- 獲取麻煩
- 數(shù)據(jù)不是響應(yīng)式
Vuex 容器(推薦)
- 獲取方便
- 響應(yīng)式的
使用容器存儲 Token 的思路:
登錄成功,將 Token 存儲到 Vuex 容器中
- 獲取方便
- 響應(yīng)式
為了持久化,還需要把 Token 放到本地存儲
- 持久化
總結(jié): Vuex狀態(tài)管理工具可有可無 (*?ω-q)
在 src/store/index.js
中
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ // 1. 存儲數(shù)據(jù)的地方 - 類比于vue文件的data() state: { // 一個對象,儲存當(dāng)前登錄用戶的token數(shù)據(jù) user: {} }, // 2. 外界修改store中state的屬性值,必須通過mutations中設(shè)置的修改方法 - 類比methods // 注意:這里方法里面的代碼和.vue文件中的書寫方式有差異,注意區(qū)分 mutations: { setUser (state, data) { state.user = data } }, // 3. 涉及到異步操作后修改state數(shù)據(jù)時,必須先過actions中的自定義方法,通過actions去調(diào)用mutations中的方法 actions: { }, // 4. 是state中數(shù)據(jù)的計算屬性 - 類比computed getters: { }, // 5. 模塊化vuex,可以讓每一個模塊擁有自己的 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 的時候,會先把其它的 toast 先清除 this.$toast.success('登錄成功') } catch (err) { ... },
3. 將 store中的 token 相關(guān)數(shù)據(jù)存儲到容器中
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ù)備份到本地存儲 window.localStorage.setItem(TOKEN_KEY, JSON.stringify(state.user)) } }, ... })
2. 優(yōu)化封裝本地存儲操作模塊 - 封裝localStrage功能
創(chuàng)建 src/utils/storage.js
模塊
- 存儲
- 獲取
- 刪除
// 封裝本地存儲操作模塊 /* 一個本地存儲的數(shù)據(jù)應(yīng)該擁有那些特性: 增刪改查 */ /* 儲存數(shù)據(jù) (新增, 修改) */ export const setItem = (key, value) => { // 將數(shù)組,對象等引用數(shù)據(jù)類型轉(zhuǎn)化為JSON字符串進(jìn)行存儲 // 將簡單數(shù)據(jù)類型直接存儲 // 需要外界使用該方法時傳入對一個的 鍵名 if (typeof value === 'object') { // 將數(shù)組,對象等引用數(shù)據(jù)類型轉(zhuǎn)化為JSON字符串進(jìn)行存儲 value = JSON.stringify(value) } window.localStorage.setItem(key, value) } /* 獲取數(shù)據(jù) */ export const getItem = key => { // 如果該鍵存儲的是引用數(shù)據(jù)類型的JSON字符串,那么需要進(jìn)行JSON.parse的轉(zhuǎn)化 const data = window.localStorage.getItem(key) // 使用JSON.parse()做JSON數(shù)據(jù)轉(zhuǎn)化時可能會出現(xiàn)報錯 // 1. 做條件判斷(要去找到所有滿足、不滿足的條件) 2. 做錯誤判斷 try { // 先嘗試做JSON.parse()的轉(zhuǎn)化,如果報錯了,在把他當(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ù)備份到本地存儲 setItem(TOKEN_KEY, state.user) } }, ... })
3. Vuex各屬性的使用
創(chuàng)建測試用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的計算屬性 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(() => { // 延時1秒提交至mutations中的方法 context.commit('setName', name) }, 1000) }, acSetAge (context, age) { setTimeout(() => { context.commit('setAge', age) }, 1000) } }, // 將store模塊化 modules: { } })
創(chuàng)建頁面comOne.vue
測試計算屬性
<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: {}, // 計算屬性computed無法傳遞參數(shù) computed: { // 映射 state 中的數(shù)據(jù)為計算屬性 ...mapState(['name', 'age']), // 映射 getters 中的數(shù)據(jù)為計算屬性 ...mapGetters(['getName', 'getAge']) } } </script> <style scoped> </style>
創(chuàng)建comTwo.vue
測試同步異步方法
<template> <div class="wrapper"> <div> <span>同步修改:</span> <!--直接回車調(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> <!--直接回車調(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 對話的方法,即:包含 $store.commit(xx) 的函數(shù) ...mapMutations(['setName', 'setAge']), // 用于生成與 actions 對話的方法,即:包含 $store.dispatch(xx) 的函數(shù) ...mapActions(['acSetName', 'acSetAge']) } } </script> <style scoped> </style>
4. 關(guān)于 Token 過期問題
登錄成功之后后端會返回兩個 Token:
token
:訪問令牌,有效期2小時refresh_token
:刷新令牌,有效期14天,用于訪問令牌過期之后重新獲取新的訪問令牌
我們的項目接口中設(shè)定的 Token
有效期是 2 小時,超過有效期服務(wù)端會返回 401
表示 Token 無效或過期了。
為什么過期時間這么短?
- 為了安全,例如 Token 被別人盜用
過期了怎么辦?
- 讓用戶重新登錄,用戶體驗太差了
- 使用
refresh_token
解決token
過期
如何使用 refresh_token
解決 token
過期?
到課程的后面我們開發(fā)的業(yè)務(wù)功能豐富起來之后,再給大家講解 Token 過期處理。
大家需要注意的是在學(xué)習(xí)測試的時候如果收到 401 響應(yīng)碼,請重新登錄。
5.優(yōu)化設(shè)置 Token
項目中的接口除了登錄之外大多數(shù)都需要提供 token 才有訪問權(quán)限。
通過接口文檔可以看到,后端接口要求我們將 token 放到請求頭 Header
中并以下面的格式發(fā)送。
字段名稱:Authorization
字段值:Bearer token
,注意 Bearer
和 token
之間有一個空格
方式一:在每次請求的時候手動添加(麻煩)。
axios({ method: "", url: "", headers: { Authorization: "Bearer token" } })
方式二:使用請求攔截器統(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)路徑 }) // 請求攔截器 // Add a request interceptor request.interceptors.request.use(function (config) { // Do something before request is sent // config :本次請求的配置對象 // config 里面有一個屬性:headers const { user } = store.state if (user && user.token) { config.headers.Authorization = `Bearer ${user.token}` } return config }, function (error) { // 如果請求出錯 - 拋出異常 // Do something with request error return Promise.reject(error) })
api.user.js
注釋掉store和獲取用戶信息攜帶的請求頭
import request from '@/utils/request' // import store from '@/store' /** * 獲取用戶自己的信息 */ export const getUserInfo = () => { return request({ method: 'GET', url: '/v1_0/user' // 發(fā)送請求頭數(shù)據(jù) // headers: { // // 注意:該接口需要授權(quán)才能訪問 // // token的數(shù)據(jù)格式:Bearer token數(shù)據(jù),注意 Bearer 后面有個空格 // Authorization: `Bearer ${store.state.user.token}` // } }) }
以上就是Vuex處理用戶Token優(yōu)化設(shè)置封裝本地存儲操作模塊的詳細(xì)內(nèi)容,更多關(guān)于Vuex處理用戶Token的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue項目安裝less和less-loader的詳細(xì)步驟
這篇文章主要介紹了Vue項目安裝less和less-loader的詳細(xì)步驟,本文分步驟結(jié)合示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-12-12vue使用路由的query配置項時清除地址欄的參數(shù)案例詳解
這篇文章主要介紹了vue使用路由的query配置項時如何清除地址欄的參數(shù),本文通過案例給大家分享完美解決方案,需要的朋友可以參考下2023-09-09