一.了解用法
1.了解(session,cookie)token
Token的引入:Token是在客戶端頻繁向服務端請求數據,服務端頻繁的去數據庫查詢用戶名和密碼並進行對比,判斷用戶名和密碼正確與否,並作出相應提示,在這樣的背景下,Token便應運而生。
token 是在服務端產生的一串字符串,以作客戶端進行請求的一個令牌。如果前端使用用戶名/密碼向服務端請求認證,服務端認證成功,那么在服務端會返回 Token 給前端。前端可以在每次請求的時候帶上 Token 證明自己的合法地位。如果這個 Token 在服務端持久化(比如存入數據庫),那它就是一個永久的身份令牌(除非設置了有效期)。
2.token 優點
Token 完全由應用管理,所以它可以避開同源策略
Token 可以避免 CSRF 攻擊
Token 可以是無狀態的,可以在多個服務間共享
減輕服務器的壓力,減少頻繁的查詢數據庫,使服務器更加健壯。
1、第一次登錄的時候,前端調后端的登陸接口,發送用戶名和密碼
2、后端收到請求,驗證用戶名和密碼,驗證成功,就給前端返回一個token
3、前端拿到token,將token存儲到localStorage和vuex中,並跳轉路由頁面
4、前端每次跳轉路由,就判斷 localStroage 中有無 token
,沒有就跳轉到登錄頁面,有則跳轉到對應路由頁面
5、每次調后端接口,都要在請求頭中加token
6、后端判斷請求頭中有無token,有token,就拿到token並驗證token,驗證成功就返回數據,驗證失敗(例如:token過期)就返回401,請求頭中沒有token也返回401
7、如果前端拿到狀態碼為401,就清除token信息並跳轉到登錄頁面
二.實際使用1
.封裝一個本地緩存的方法
在src下新建一個storage文件夾,文件夾里新建index.vue
代碼如下:
// 封裝操作localstorage本地存儲的方法 模塊化
var storage = { set(key, value) { localStorage.setItem(key, JSON.stringify(value)) }, get(key) { return localStorage.getItem(key) != 'undefined' ? JSON.parse(localStorage.getItem(key)) : undefined }, getForIndex(index) { return localStorage.key(index) }, getKeys() { let items = this.getAll() let keys = [] for (let index = 0; index < items.length; index++) { keys.push(items[index].key) } return keys }, getLength() { return localStorage.length }, getSupport() { return (typeof (Storage) !== 'undefined') }, remove(key) { localStorage.removeItem(key) }, removeAll() { localStorage.clear() }, getAll() { let len = localStorage.length // 獲取長度 let arr = new Array(len) // 定義數據集 for (var i = 0; i < len; i++) { // 獲取key 索引從0開始 var getKey = localStorage.key(i) // 獲取key對應的值 var getVal = localStorage.getItem(getKey) // 放進數組 arr[i] = { 'key': getKey, 'val': getVal } } return arr } } export default storage
2.封裝好storage后,把他掛載到全局組件中
打開src下的main.js
加上一句代碼:
Vue.prototype.$storage = storage;
- 1
3.在request.js里放入token
至於request是什么,具體可以參考另一篇文檔:“Vue如何成功調用一個動態數據接口+解決跨域問題”:該文檔鏈接點擊跳轉
其中的方法二具體描述了request.js
每次跳轉路由前,判斷 localStroage(或vuex) 中有無 token,或者是否過期(可以寫在封裝Axios的請求攔截器中或者router的路由守衛中)
request.js代碼如下
import axios from 'axios' import storage from '@/storage' import router from '@/router' // create an axios instance const service = axios.create({ baseURL: '/api', // url = base url + request url timeout: 5000 // request timeout }) // 添加請求攔截器,若token存在則在請求頭中加token,不存在也繼續請求 service.interceptors.request.use( config => { // 每次發送請求之前檢測都vuex存有token,那么都要放在請求頭發送給服務器,沒有則不帶token // Authorization是必須的 let tokenInfo = storage.get('TOKEN') const token = tokenInfo ? tokenInfo.accessToken : null const tokenType = token ? tokenInfo.tokenType.substring(0, 1).toUpperCase() + tokenInfo.tokenType.substring(1) + ' ' : null if (token && tokenType) { config.headers.Authorization = tokenType + token } return config }, error => { console.log('在request攔截器檢查到錯誤:', error.response) return Promise.reject(error) } ) // respone攔截器 service.interceptors.response.use( response => { return response }, error => { // 在status不正確的情況下,判別status狀態碼給出對應響應 if (error.response) { console.log('在respone攔截器檢查到錯誤:') switch (error.response.status) { case 204: error.response.data.error = '204:No Content(沒有內容)' break case 401: // 可能是token過期,清除它 storage.remove('tokenInfo') location.reload() // 刷新頁面,觸發路由守衛 error.response.data.error = '401:Unauthorized(未經授權)' break case 403: error.response.data.error = '403:Forbidden(被禁止的)' break case 500: error.response.data.error = '500:服務器內部錯誤' break default: return error } return Promise.reject(error.response.data.error) } return Promise.reject(error) } ) export default service
4.在路由,router下的index.vue里設置路由守衛
整個index代碼如下:
import Vue from 'vue' import Router from 'vue-router' import Login from '@/views/login'; import Main from '@/main/index'; import tip1 from '@/views/tip1'; import tip2 from '@/views/tip2'; import tip3 from '@/views/tip3'; import storage from '@/storage' Vue.use(Router) const routes = [{ path: '/', name: 'Login', // redirect: '/login', component: Login, }, { path: "/login", component: Login, }, { path: '/Main', component: Main, children: [{ path: '/', name: 'Tip1', component: tip1 }, { path: '/tip1', name: 'Tip1', component: tip1 }, { path: '/tip2', name: 'Tip2', component: tip2, meta: { requireAuth: true } }, { path: '/tip3', name: 'Tip3', component: tip3 }, ] } ] const router = new Router({ routes }) // 設置路由守衛,在進頁面之前,判斷有token,才進入頁面,否則返回登錄頁面 router.beforeEach((to, from, next) => { // 默認requiresAuth為false才不需要登錄,其他都要 // to.matched.some(r => r.meta.requireAuth) or to.meta.requiresAuth if (to.matched.some(r => r.meta.requireAuth) !== false) { let tokenInfo = storage.get('TOKEN') if (tokenInfo) { console.log("有token") next(); } else { storage.remove('TOKEN') next({ path: "/login", query: { redirect: to.fullPath } // 將剛剛要去的路由path(卻無權限)作為參數,方便登錄成功后直接跳轉到該路由 }); } } else { next(); //如果無需token,那么隨它去吧 } }); //暴露router實例 export default router // 1.將調用登錄接口成功以后,把后端傳過來的token放入本地緩存 // 2.路由跳轉之前執行路由守衛,實例化一個Router對象,使用該對象內置方法beforeEach,在路由跳轉前判斷該頁面是否設置了token,獲取token // 如果有token,next()繼續執行路由跳轉 // 如果沒有token,跳轉去登錄界面
哪個頁面需要路由守衛。就給他加上參數
meta: { requireAuth: true }
轉載、文章來源:https://blog.csdn.net/weixin_46166949/article/details/105856745