翻譯原文連接:https://scotch.io/tutorials/handling-authentication-in-vue-using-vuexjavascript
個人翻譯小站:https://www.zcfy.cc/article/handling-authentication-in-vue-using-vuexvue
傳統方式中,許多人使用本地存儲,來管理經過客戶端驗證生成的tokens。一個大問題是如何有更好的方式,來管理驗證tokens,從而容許咱們來存儲更大的用戶信息。java
對於常常檢查本地存儲來講,聽起來是個更好的選擇?讓咱們一塊兒來探索下吧。ios
對於這個項目,咱們想建立一個使用vuex和vue-router的vue應用。咱們會使用vue cli 3.0 來建立一個vue項目,並從選項中選擇路由和vuex。git
執行下面的命令開始建立:github
$ vue create vue-auth
按照對話框的提示,添加必要的信息,並選擇咱們須要的選項,完成安裝。vue-router
下一步, 安裝axios:vuex
$ npm install axios --save
咱們在許多組件中都須要用到axios。讓咱們在全局總體來配置它,這樣當咱們須要它的時候,不用每次都去引入。npm
打開 ./src/main.js
文件,而且添加下面:
[...] import store from './store' import Axios from 'axios' Vue.prototype.$http = Axios; const token = localStorage.getItem('token') if (token) { Vue.prototype.$http.defaults.headers.common['Authorization'] = token } [...]
如今,當咱們想在組件內使用axios時, 咱們能夠用this.$http
,這樣至關於直接是axios。咱們也能夠在axios頭部給本身的token, 設置身份驗證
,這樣若是token是必需的,咱們的請求將處於控制中。在這種方式下,當咱們想要發送請求時,任什麼時候候都不用設置token。
相關課程: Vue建立一個網上商店
完成以後,讓咱們使用服務器來處理身份驗證。
我已經寫過關於這個,在我解釋如何用vue-router來解決身份驗證時。仔細看看Setup Node.js Server 這個章節。
建立Login.vue
在 ./src/components
目錄下。 以後, 給登陸頁面添加模板:
<template> <div> <form class="login" @submit.prevent="login"> <h1>Sign in</h1> <label>Email</label> <input required v-model="email" type="email" placeholder="Name"/> <label>Password</label> <input required v-model="password" type="password" placeholder="Password"/> <hr/> <button type="submit">Login</button> </form> </div> </template>
當你作完以後, 添加data屬性,將其綁定到HTML表單中:
[...] <script> export default { data(){ return { email : "", password : "" } }, } </script>
如今, 讓咱們給登陸添加方法:
[...] <script> export default { [...] methods: { login: function () { let email = this.email let password = this.password this.$store.dispatch('login', { email, password }) .then(() => this.$router.push('/')) .catch(err => console.log(err)) } } } </script>
咱們正在使用vuex的action — login
來解決身份驗證。咱們能夠在將actions細化到回調裏面,這樣就能夠在本身的組件裏面作一些很酷的事情了。
跟login組件相似,那咱們給註冊用戶弄一個了。在組件目錄裏面建立Register.vue
,並將下面的添加進去:
<template> <div> <h4>Register</h4> <form @submit.prevent="register"> <label for="name">Name</label> <div> <input id="name" type="text" v-model="name" required autofocus> </div> <label for="email" >E-Mail Address</label> <div> <input id="email" type="email" v-model="email" required> </div> <label for="password">Password</label> <div> <input id="password" type="password" v-model="password" required> </div> <label for="password-confirm">Confirm Password</label> <div> <input id="password-confirm" type="password" v-model="password_confirmation" required> </div> <div> <button type="submit">Register</button> </div> </form> </div> </template>
讓咱們定義一下這些將綁定到表單裏面的data屬性:
[...] <script> export default { data(){ return { name : "", email : "", password : "", password_confirmation : "", is_admin : null } }, } </script>
如今,讓咱們添加方法進去:
[...] <script> export default { [...] methods: { register: function () { let data = { name: this.name, email: this.email, password: this.password, is_admin: this.is_admin } this.$store.dispatch('register', data) .then(() => this.$router.push('/')) .catch(err => console.log(err)) } } } </script>
讓咱們建立一個普通的組件,它在用戶經過驗證後會顯示。文件命名爲Secure.vue
,並添加下面的進去:
<template> <div> <h1>This page is protected by auth</h1> </div> </template> 更新App組件
打開./src/App.vue
文件,並添加下面的進去:
<template> <div id="app"> <div id="nav"> <router-link to="/">Home</router-link> | <router-link to="/about">About</router-link><span v-if="isLoggedIn"> | <a @click="logout">Logout</a></span> </div> <router-view/> </div> </template>
若是用戶登陸進去後,你能看到關聯的Logout
了 嗎?很好。
如今,讓咱們給logout添加邏輯。
<script> export default { computed : { isLoggedIn : function(){ return this.$store.getters.isLoggedIn} }, methods: { logout: function () { this.$store.dispatch('logout') .then(() => { this.$router.push('/login') }) } }, } </script>
當用戶點擊退出按鈕時,咱們其實在作兩件事 — 計算用戶驗證的狀態和分發vuex store裏面的退出事件。在退出以後,咱們利用 this.$router.push('/login')
,切換用戶到 login
頁面。固然你能夠改變任何你想讓用戶跳轉的地方。
就是這樣了。讓咱們用vuex構建權限模塊。
若是你讀過之前的Setup Node.js Server **部分, 你應該注意到咱們須要在本地存儲用戶權限token,同時,當用戶被授予權限後,咱們隨時須要從新獲得token以及用戶信息。
首先, 讓咱們給vuex建立 store.js
文件:
import Vue from 'vue' import Vuex from 'vuex' import axios from 'axios' Vue.use(Vuex) export default new Vuex.Store({ state: { status: '', token: localStorage.getItem('token') || '', user : {} }, mutations: { }, actions: { }, getters : { } })
若是你注意到,咱們同時引入了vue,vuex和axios,以後讓vue使用vuex,這是由於它是很重要的一步。
咱們已經定義了state的屬性。如今vuex的state可以支持驗證狀態, jwt
token以及用戶信息。
Vuex actions裏面主要是提交更改到vuex的store裏面。咱們將建立一個login
的action,它將使用服務器對用戶進行身份驗證,並向vuex存儲提交用戶憑據。打開./src/store.js
文件,並添加下面到actions對象中:
login({commit}, user){ return new Promise((resolve, reject) => { commit('auth_request') axios({url: 'http://localhost:3000/login', data: user, method: 'POST' }) .then(resp => { const token = resp.data.token const user = resp.data.user localStorage.setItem('token', token) axios.defaults.headers.common['Authorization'] = token commit('auth_success', token, user) resolve(resp) }) .catch(err => { commit('auth_error') localStorage.removeItem('token') reject(err) }) }) },
登陸action經過vuex commit
驗證,咱們將用它進行觸發更改。vuex store裏面能記錄這些更改的變化。
咱們正在調用服務器的登陸路徑並返回必要的數據。咱們在本地存儲token,以後經過auth_success
來更新存儲用戶信息和token。在這一點上,咱們也在頭部設置了axios
。
咱們能夠在vuex store中存儲token,可是若是用戶離開咱們的應用,全部在vuex裏面的存儲都將消失。爲了確保用戶在有效時間內不用再重複登陸,咱們只能將token進行本地存儲。
重要的是你知道這些是如何工做的,這樣你就能決定你到底想要實現什麼。
咱們返回一個promise,這樣咱們能在用戶登陸完成後,作出響應。
註冊
事件像 login
事件, the register
事件是同一種工做方式。在相同的文件中,添加下面的到actions對象裏面:
register({commit}, user){ return new Promise((resolve, reject) => { commit('auth_request') axios({url: 'http://localhost:3000/register', data: user, method: 'POST' }) .then(resp => { const token = resp.data.token const user = resp.data.user localStorage.setItem('token', token) axios.defaults.headers.common['Authorization'] = token commit('auth_success', token, user) resolve(resp) }) .catch(err => { commit('auth_error', err) localStorage.removeItem('token') reject(err) }) }) },
它與login
事件工做方式很像,。稱之爲有共同的mutators的 login
和register
,具備相同的目標——讓用戶進入系統。
退出
事件咱們但願用戶可以退出系統,同時,咱們但願銷燬上一次驗證的會話數據。在同一個actions
對象中,添加下面:
logout({commit}){ return new Promise((resolve, reject) => { commit('logout') localStorage.removeItem('token') delete axios.defaults.headers.common['Authorization'] resolve() }) }
如今,當用戶點擊退出時,咱們將移除以前在 axios
頭部設置的jwt
token 。他們如今將沒法執行須要token的事務。
像我以前提到的,mutators是被用來改變vuex store的狀態。讓咱們在應用中給用過的mutators定義。在mutators對象中,添加下面的:
mutations: { auth_request(state){ state.status = 'loading' }, auth_success(state, token, user){ state.status = 'success' state.token = token state.user = user }, auth_error(state){ state.status = 'error' }, logout(state){ state.status = '' state.token = '' }, },
咱們使用getter來獲取vuex狀態中的屬性值。在這種狀況下,getter的做用是將應用程序數據與應用程序邏輯分離,並確保咱們不會泄露敏感信息。
添加下面的到getters
對象中:
getters : { isLoggedIn: state => !!state.token, authStatus: state => state.status, }
你會贊成個人觀點,這是一種更簡潔的訪問存儲數據的方式☺️.
這篇文章的整個目的是實現身份驗證,讓沒有權限的用戶看不到某些頁面。爲了實現這個,咱們須要知道用戶想要訪問的頁面,以及當用戶被受權時,咱們有必定的方法來檢驗它。咱們同時須要必定的方式,若是某些頁面,受權或者未受權的用戶能夠單獨或者同時訪問的。這些都是很重要的考慮條件,幸運地是,咱們能夠經過vue-router來講實現。
打開 ./src/router.js
文件,並引入咱們須要的這些:
import Vue from 'vue' import Router from 'vue-router' import store from './store.js' import Home from './views/Home.vue' import About from './views/About.vue' import Login from './components/Login.vue' import Secure from './components/Secure.vue' import Register from './components/Register.vue' Vue.use(Router)
正如你看到的這樣,咱們已經引入vue,vue-router和咱們建立的vuex。咱們同時還引入了定義的全部組件,並設置vue中使用路由。
讓咱們定義路由:
[...] let router = new Router({ mode: 'history', routes: [ { path: '/', name: 'home', component: Home }, { path: '/login', name: 'login', component: Login }, { path: '/register', name: 'register', component: Register }, { path: '/secure', name: 'secure', component: Secure, meta: { requiresAuth: true } }, { path: '/about', name: 'about', component: About } ] }) export default router
咱們路由的定義是很廣泛的。對於須要權限驗證的路由,咱們須要增長額外的數據,確保當用戶訪問它時,咱們能夠識別它。這是添加到路由定義中的元屬性的本質。若是你想問_」我能夠添加更過的數據給元數據並使用它嗎?」,我很堅決的告訴你,這是絕對的😁。
咱們有本身的路由定義。如今,讓咱們檢驗未受權訪問並採起行動。在 router.js
文件中,添加下面的在 export default router
以前:
router.beforeEach((to, from, next) => { if(to.matched.some(record => record.meta.requiresAuth)) { if (store.getters.isLoggedIn) { next() return } next('/login') } else { next() } })
從這篇文章,經過使用vue router來進行身份驗證,你能夠回想一下咱們這裏有一個很是複雜的機制,它變得很是大,變得很是混亂。vuex已經幫咱們簡化了它,咱們能夠繼續給路由添加任何條件。在咱們的vuex存儲中,咱們能夠定義操做來檢查這些條件並獲取返回它們的值。
由於咱們在本地存儲token,它能夠一直保留着。這意味着不管什麼時候,咱們打開本身的應用,它能夠自動的驗證用戶,即便token已通過期失效。最多的狀況是,咱們的請求會由於無效token而持續失敗。這對於用戶是個很差的體驗。
如今, 打開./src/App.vue
文件並在script裏面,添加下面的:
export default { [...] created: function () { this.$http.interceptors.response.use(undefined, function (err) { return new Promise(function (resolve, reject) { if (err.status === 401 && err.config && !err.config.__isRetryRequest) { this.$store.dispatch(logout) } throw err; }); }); } }
咱們截獲axios請求,已肯定是否獲取到401未受權
響應。若是這麼作,咱們分發 logout
事件,那麼用戶得到退出應用。這會讓用戶跳轉到以前設計的 login
頁面,這樣他們能夠再次登陸。
我贊同這樣會提高用戶體驗 ☺️.
從之前的文章來看,您能夠看到,基於vuex的引入,咱們目前的應用程序發生了重大變化。如今,咱們不依賴於一直檢查token,無論到哪裏都有混亂的條件。咱們能夠簡單地使用vuex存儲來管理權限,而且只需使用幾行代碼檢查應用程序中的狀態。
我但願這能夠幫助您創建更好的應用。