【翻译】使用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存储来管理权限,而且只需使用几行代码检查应用程序中的状态。

我但愿这能够帮助您创建更好的应用。

相关文章
相关标签/搜索