使用Vuex解决Vue中的身份验证问题


Posted in Javascript onSeptember 28, 2018

传统方式中,许多人使用本地存储,来管理通过客户端验证生成的tokens。一个大问题是如何有更好的方式,来管理验证tokens,从而允许我们来存储更大的用户信息。

这就是Vuex的作用。 Vuex为Vue.js应用管理状态.。对于应用中所有的组件来说,它被当做中央存储,并用规则确保状态只能以可预见的方式改变。

对于经常检查本地存储来说,听起来是个更好的选择?让我们一起来探索下吧。

建立应用模块

对于这个项目,我们想创建一个使用vuex和vue-router的vue应用。我们会使用vue cli 3.0 来创建一个vue项目,并从选项中选择路由和vuex。

执行下面的命令开始创建:

$ vue create vue-auth

按照对话框的提示,添加必要的信息,并选择我们需要的选项,完成安装。

下一步, 安装axios:

$ npm install axios --save 

配置Axios

我们在许多组件中都需要用到axios。让我们在全局整体来配置它,这样当我们需要它的时候,不用每次都去引入。

打开 ./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构建权限模块。

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

以上所述是小编给大家介绍的使用Vuex解决Vue中的身份验证问题,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
JavaScript中Function()函数的使用教程
Jun 04 Javascript
基于ajax实现文件上传并显示进度条
Aug 03 Javascript
原生js封装二级城市下拉列表的实现代码
Jun 16 Javascript
快速解决js中window.location.href不工作的问题
Nov 02 Javascript
js实现开启密码大写提示
Dec 21 Javascript
完美解决linux下node.js全局模块找不到的情况
May 16 Javascript
node中modules.exports与exports导出的区别
Jun 08 Javascript
vue store之状态管理模式的详细介绍
Jun 13 Javascript
JS中的算法与数据结构之常见排序(Sort)算法详解
Aug 16 Javascript
Node 模块原理与用法详解
May 13 Javascript
vantUI 获得piker选中值的自定义ID操作
Nov 04 Javascript
Nuxt的动态路由和参数校验操作
Nov 09 Javascript
js限制输入框只能输入数字(onkeyup触发)
Sep 28 #Javascript
js限制input只能输入有效的数字(第一个不能是小数点)
Sep 28 #Javascript
js实现点击展开隐藏效果(实例代码)
Sep 28 #Javascript
javascript中toFixed()四舍五入使用方法详解
Sep 28 #Javascript
对vue中v-if的常见使用方法详解
Sep 28 #Javascript
总结javascript三元运算符知识点
Sep 28 #Javascript
2种在vue项目中使用百度地图的简单方法
Sep 28 #Javascript
You might like
php中禁止单个IP与ip段访问的代码小结
2012/07/04 PHP
PHP简单实现HTTP和HTTPS跨域共享session解决办法
2015/05/27 PHP
四个常见html网页乱码问题及解决办法
2015/09/08 PHP
服务器迁移php版本不同可能诱发的问题
2015/12/22 PHP
Yii2中设置与获取别名的函数(setAlias和getAlias)用法分析
2016/07/25 PHP
PHP SFTP实现上传下载功能
2017/07/26 PHP
thinkphp5实现微信扫码支付
2019/12/23 PHP
javascript字符串替换及字符串分割示例代码
2013/12/12 Javascript
深入理解ECMAScript的几个关键语句
2016/06/01 Javascript
Angularjs中ng-repeat-start与ng-repeat-end的用法实例介绍
2016/12/31 Javascript
Vue系列:通过vue-router如何传递参数示例
2017/01/16 Javascript
正则验证小数点后面只能有两位数的方法
2017/02/28 Javascript
javaScript 逻辑运算符使用技巧整理
2017/05/03 Javascript
vue v-model表单控件绑定详解
2017/05/17 Javascript
详解ES6 系列之异步处理实战
2018/10/26 Javascript
基于vue的验证码组件的示例代码
2019/01/22 Javascript
JS浮点数运算结果不精确的Bug解决
2019/08/01 Javascript
详解mpvue开发微信小程序基础知识
2019/09/23 Javascript
vue 实现移动端键盘搜索事件监听
2019/11/06 Javascript
jquery添加div实现消息聊天框
2020/02/08 jQuery
Python文件读取的3种方法及路径转义
2015/06/21 Python
Python通过90行代码搭建一个音乐搜索工具
2015/07/29 Python
各个系统下的Python解释器相关安装方法
2015/10/12 Python
用Python批量把文件复制到另一个文件夹的实现方法
2019/08/16 Python
python3用PyPDF2解析pdf文件,用正则匹配数据方式
2020/05/12 Python
利用css3画个同心圆示例代码
2017/07/03 HTML / CSS
专注澳大利亚特产和新西兰特产的澳洲中文网:0061澳洲制造
2019/03/24 全球购物
运动会广播稿80字
2014/01/23 职场文书
六一亲子活动总结
2014/07/01 职场文书
六查六看自检自查剖析材料
2014/10/14 职场文书
销售助理岗位职责
2015/02/11 职场文书
支行行长岗位职责
2015/02/15 职场文书
严以律己专题学习研讨会发言材料
2015/11/09 职场文书
2016年综治宣传月活动宣传标语口号
2016/03/16 职场文书
子女赡养老人协议书
2016/03/23 职场文书
Kubernetes中Deployment的升级与回滚
2022/04/01 Servers