【翻譯】使用Vuex解決Vue中的身份驗證

翻譯原文連接:https://scotch.io/tutorials/handling-authentication-in-vue-using-vuexjavascript

個人翻譯小站:https://www.zcfy.cc/article/handling-authentication-in-vue-using-vuexvue

 

傳統方式中,許多人使用本地存儲,來管理經過客戶端驗證生成的tokens。一個大問題是如何有更好的方式,來管理驗證tokens,從而容許咱們來存儲更大的用戶信息。java

這就是Vuex的做用。 Vuex爲Vue.js應用管理狀態.。對於應用中全部的組件來講,它被當作中央存儲,並用規則確保狀態只能以可預見的方式改變。node

對於常常檢查本地存儲來講,聽起來是個更好的選擇?讓咱們一塊兒來探索下吧。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

咱們在許多組件中都須要用到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-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構建權限模塊。

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登陸事件

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,這樣咱們能在用戶登陸完成後,作出響應。

建立Vuex註冊事件

像 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 ,具備相同的目標——讓用戶進入系統。

建立Vuex退出事件

咱們但願用戶可以退出系統,同時,咱們但願銷燬上一次驗證的會話數據。在同一個actions對象中,添加下面:

logout({commit}){
  return new Promise((resolve, reject) => {
    commit('logout')
    localStorage.removeItem('token')
    delete axios.defaults.headers.common['Authorization']
    resolve()
  })
}

如今,當用戶點擊退出時,咱們將移除以前在 axios頭部設置的jwt token 。他們如今將沒法執行須要token的事務。

建立Mutations

像我以前提到的,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 = ''
  },
},

建立Getters

咱們使用getter來獲取vuex狀態中的屬性值。在這種狀況下,getter的做用是將應用程序數據與應用程序邏輯分離,並確保咱們不會泄露敏感信息。

添加下面的到getters 對象中:

getters : {
  isLoggedIn: state => !!state.token,
  authStatus: state => state.status,
}

你會贊成個人觀點,這是一種更簡潔的訪問存儲數據的方式☺️.

在Auth後面隱藏頁面

這篇文章的整個目的是實現身份驗證,讓沒有權限的用戶看不到某些頁面。爲了實現這個,咱們須要知道用戶想要訪問的頁面,以及當用戶被受權時,咱們有必定的方法來檢驗它。咱們同時須要必定的方式,若是某些頁面,受權或者未受權的用戶能夠單獨或者同時訪問的。這些都是很重要的考慮條件,幸運地是,咱們能夠經過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已通過期失效。最多的狀況是,咱們的請求會由於無效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存儲來管理權限,而且只需使用幾行代碼檢查應用程序中的狀態。

我但願這能夠幫助您創建更好的應用。

相關文章
相關標籤/搜索