【翻译】使用Vuex解决Vue中的身份验证
翻译原文链接:https://scotch.io/tutorials/handling-authentication-in-vue-using-vuex
我的翻译小站:https://www.zcfy.cc/article/handling-authentication-in-vue-using-vuex
传统方式中,许多人使用本地存储,来管理通过客户端验证生成的tokens。一个大问题是如何有更好的方式,来管理验证tokens,从而允许我们来存储更大的用户信息。
对于经常检查本地存储来说,听起来是个更好的选择?让我们一起来探索下吧。
建立应用模块
对于这个项目,我们想创建一个使用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
我们路由的定义是很普遍的。对于需要权限验证的路由,我们需要增加额外的数据,确保当用户访问它时,我们可以识别它。这是添加到路由定义中的元属性的本质。如果你想问_”我可以添加更过的数据给元数据并使用它吗?”,我很坚定的告诉你,这是绝对的