Vuex身份认证
虽说上一节我们实现了登录功能,但是实际上还是可以通过浏览器的地址来跳过登录访问到后台,这种可有可无的登录功能使得系统没有安全性,而且没有意义
为了让登录这个功能有意义,我们应该:
- 应当在用户登录成功之后给用户生成一个标记(令牌),将这个令牌保存起来
- 在用户访问任意需要登录的页面(组件)的时候都要去验证令牌
- 从而识别到用户是否登录或者是否有权限去访问对应的功能
1.成功时,访问组件
2.失败时,进行提示
如何让login组件中的数据被任意其他组件访问呢?这个时候可以使用vue官方的状态管理工具Vuex
Vuex
Vuex是一个专门为Vue.js应用程序开发的状态管理模式
文档:
https://vuex.vuejs.org/zh
- Vuex是专门为Vue.js设计的状态管理库
- 采用集中式的方式存储需要共享的数据
- 本质上就是一个JavaScript库
- 用来解决复杂组件通信,数据共享的问题
简单来说就是Vuex用来统一存储需要在多个组件间共享的状态(数据),状态可以被任意组件操作,使得组件通信变得易如反掌

那么归根到底,我们是否需要Vuex,要根据什么来判断呢?
- 多个视频依赖于同一个状态
- 来自不同视图的行为需要变更同一状态
安装Vuex
通过npm安装:
npm install vuex -S
使用Vue CLI创建项目的时候,可以在项目选项中选择Vuex,这个时候就不需要再单独安装了,这边我们已经在创建项目的时候安装过了Vuex,所以不再多做操作
使用
创建Vuex实例store,store通常称之为"容器"
文件,store/index.js


通过Vue.use()引入Vuex中,Vuex的功能被注入到根实例下的所有子组件中,可以通过$store访问到内部功能
我们的项目通过VueCLI创建时已经选择了Vuex,所以创建和引入都已经被Vue CLI自动完成了
State
容器中的state用于存储需要在组件之间共享的数据
- 容器中的数据可以被任意组件访问
- 容器中的数据为响应式数据
state存储count
Vue官方调试工具也可以看到数据
在组件中,通过vm.$store.state.状态名
访问
// login/index.vue
methods: {async onSubmit () {console.log(this.$store.state.user)...}
}
Mutation

简单来说,要修改Vuex的state,必须提前定义Mutation函数,需要的时候再进行提交,Mutation接收state对象作为第一个参数,用于操作state内部的数据
// store/index.js
export default new Vuex.Store({state: {user: 100},mutations: {setUser (state) {state.user++}},actions: {},modules: {}
})
在组件中通过vm.$store.commit('Mutation名称')
提交Mutation,执行操作
// login/index.vue
methods: {async onSubmit () {console.log(this.$store.state.user)this.$store.commit('setUser')console.log(this.$store.state.user)...}
}
Mutation还接受提交载荷(payload)作为第二个参数,指的是commit()传入的额外数据,常常需要根据上下文数据修改state使用
// store/index.js
mutations: {setUser (state, payload) {state.user = payload}
},
// login/index.vue
methods: {async onSubmit () {this.$store.commit('setUser', '示例内容')...}
}
但是实际上在大多数情况下,载荷应该是一个对象才对,这样就可以包含多个字段并且记录的mutation会更易读:
mutations: {increment (state, payload) {state.count += payload.amount}
}
store.commit('increment', {amount: 10
})

Mutation的设置方式使得Vuex的状态修改有迹可循,易于维护,如果state可以通过赋值修改,一旦出错了就找不到错误点了
除此之外,Vue Devtools还提供了Vuex更高级的调试方式Time Travel

Mutation必须为同步函数,由于DevTools提供了Mutation日志功能,为了确保功能正常,内部不能存在异步任务,否则DevTools将无法得知Mutation的准确调用顺序,如果需要进行异步操作,那么则需要Vuex的Action
Action
Action类似于Mutation,不同的地方在于:
- Action提交的是Mutation,而不是直接变更状态
- Action可以包含任意的异步操作
Action 函数接受一个与store实例具有相同方法和属性的context对象,因为可以调用context.commit
提交一个Mutation
// store/index.js
actions: {addAction (context) {setTimeout(function () {context.commit('setUser')}, 1000)}
},
在实机操作中,我们经常用到ES2015的参数结构来简化代码(就比如我们在解构后端传来的数据一样,直接{ data }),这种情况尤其是出现在我们需要多次调用commit
的时候
actions: {// jia (context) {// context.commit('jia')// }jia ({ commit }) {commit('jia')}},
Action 通过 vm.$store.dispatch
方法触发,参数1为action名称,参数2为payload
// login/index.vue
methods: {async onSubmit () {this.$store.dispatch('addAction')...}
}
Vuex 核心概念还有Getter和Module,可以通过文档来进行学习
身份认证
登录状态存储
获取能够在任意组件中访问用户的登录信息,我们将状态存储在Vuex中
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {user: null},mutations: {setUser (state, payload) {state.user = payload}},actions: {},modules: {}
})
声明Mutation拿来用于修改user数据,具体内容采用payload载荷方式传入,我们可以通过devtools调试工具查看
观察到通过接口传来的数据,我们应该拿来存入的是data.content,内部为用户的相关信息。
// login/index.js
methods: {async onSubmit () {try {...// 当登录成功时,记录登录状态,存储到 Vuex 中this.$store.commit('setUser', data.content)this.$router.push({name: 'home'})this.$message.success('登录成功')} catch (err) {console.log('验证失败', err)}}
}
那么又因为传来的数据是JSON格式,为了以后方便我们的使用,我们应该在Mutation的setUser中转换为对象保存,可以通过DevTools调试工具查看到,然后就是通过本地存储的方式对user进行数据持久化,避免页面刷新后丢失,存储成功之后呢,就可以将user的初始值更改为本地存储获取user的数据
state: {// 用于登录成功后保存用户信息的(初始值尝试读取本地存储user: JSON.parse(window.localStorage.getItem('user') || null)},mutations: {// 存储用户数据setUser (state, payload) {// 将payload转换为对象后进行存储state.user = JSON.parse(payload)// 将payload的数据添加到本地存储中// 本地存储只能存储字符串window.localStorage.setItem('user', payload)}},
校验页面访问权限
路由跳转时,需要校验登录状态,根据结果进行后续处理
这里使用Vue Router的导航守卫beforeEach,在任务导航被触发的时候进行登录状态监测,当前后台页面都需要登录状态,但是有些页面不需要登录状态的话,这个该如何处理呢?
使用:Vue Router的路由元信息功能来设置
下面给需要设置登录状态的路由添加路由元信息(比如我们把home页面的子路由的部分设置为需要登录)
- meta用于保存与路由相关的自定义数据
-
requiresAuth表示是否需要认证,true为需要
用户登录状态保存在store(Vuex)中,需要引入文件来读取数据检测,在导航守卫中检测to的路由是否需要登录
// 全局前置守卫
router.beforeEach((to, from, next) => {// 验证 to 路由记录是否需要进行身份认证if (to.matched.some(record => record.meta.requiresAuth)) {// 验证Vuex 的 store 中的用户信息是否存储if (!store.state.user) {// 未登录,跳转到登录页面return next({ name: 'login' })}// 已经登录,允许通过next()} else {// 无需登录,允许通过next() // 确保一定要调用 next()}
})
登录后跳转到上次访问页面
上一次我如果访问了用户管理/user,过了一段时间状态过期,直接访问路由/user,跳转到login,登陆之后又跳转了首页。如果我希望登录后直接跳转到user而不是首页,我们就应该在每次跳转到/login时记录当前to目标的路由信息,这个时候就可以通过跳转路由的query属性进行设置
//router/index.jsif (!store.state.user) {// 未登录,跳转到登录页面next({name: 'login',query: {// 将本次路由的fullpath传递给login页面// path仅包含路径,fullpath为完整url(包含了查询字符串参数等信息)redirect: to.fullPath}})}
那么在登录页中,登陆成功的跳转时,应该检测是否存在登录前的页面信息,有就跳转,没有就默认跳转首页

除了登录过期以外,例如将页面存储书签,或者点击其他人发送的链接访问,都可以在登陆之后自动跳转到对应的路由,提高了我们的体验

用户信息与接口鉴权
用户基本信息接口:接口
首先使用postman进行接口测试
在集合中添加一个新的请求,设置基本信息

发送请求,发现传回来的是错误信息

这个时候就说明接口需要授权才能访问,查询了接口的说明文档可以得知,需要一个名为“Authotization”的请求参数(位于请求头),用于验证 授权信息,这种验证接口的授权的处理方式我们称之为接口鉴权

得出两条结论:
- 后端提供的接口是没法随便访问的
- 使用接口前需要进行接口的鉴权处理
那么我们该怎么获得权限去获取后端返回的数据呢?
Token
一种常用的接口鉴权方式
Token是在用户登陆成功之后,由服务端生成的一段保存了用户身份信息,加密的字符串
生成之后,通过响应方式将token信息响应到服务端,通过之前的登录接口响应成功时可以看到



Postman统一设置token
后续我们要用到的这个集合的接口会更多,每次都写Token的话就要被烦到,所以我们可以使用Postman提供的统一设置方式



保存了之后,回到用户信息接口会发现我们单独设置的authorization提示了已经被统一设置的信息,我们就可以将自己设置的给删除掉,再次发送请求也是没有问题的
实现用户信息展示
测试处理完毕之后,我们需要在代码中进行Token处理和功能实现
首先要封装用于用户信息请求的方法,在header最后那个设置Token,引入store用于读取token

接着在app-header组件中引入功能,并且在created钩子函数中请求数据,并将其数据绑定到视图中
<script>
// 引入用户信息接口功能
import { getUserInfo } from '@/services/user'export default {name: 'AppHeader',created () {// 加载用户信息// 钩子函数不建议书写代码逻辑,最好直接使用封装好的函数this.loadUserInfo()},data () {return {// 用户信息userInfo: {}}},methods: {async loadUserInfo () {const { data } = await getUserInfo()// console.log(data)this.userInfo = data.content}}
}
</script>

通过请求拦截器设置Token
通过Axios的请求拦截器进行统一设置Token
很多请求都是需要在header设置Token信息的,可以通过Axios拦截器进行统一处理
Axios拦截器与导航守卫相似,可以在任意请求和响应前进行拦截处理,功能分为:
- 请求拦截器
- 响应拦截器
通过请求拦截器参数config.headers可以访问请求头,将store中的Token统一设置就可以了
// 设置请求拦截器
request.interceptors.request.use(function (config) {
// 判断config.url的前缀是什么,然后进行请求baseURL的设置config.baseURL = getBaseURL(config.url)// 统一的token信息设置// 为了严谨,可以读取store中的user后进行Token检测const { user } = store.stateif (user && user.access_token) {// 设置tokenconfig.headers.Authorization = user.access_token}return config
})
这个拦截器设置之后呢,services/user.js中的getUserInfo内部的Token设置就可以删除了,同时也可以去掉store的引入
// services/user.js
...
// import store from '@/store'
...
// 用户基本信息请求
export const getUserInfo = () => {return request({method: 'GET',url: '/front/user/getInfo'// 在 header 中设置 Token 信息(统一设置后去除,记得去除上一行的分号)/* headers: {Authorization: store.state.user.access_token} */})
}
用户退出
首先我们要给退出按钮设置点击事件,发现其实是触发不了的,那是因为我们使用的是Element组件,这里的退出按钮是组件,而组件设置的都是自定义事件
// app-header.vue
<el-dropdown-itemdivided@click="handleLogout"
>退出</el-dropdown-item>...methods: {...// 退出按钮功能handleLogout () {console.log('点击退出')}
}
我们可以使用事件修饰符.native来监听组件根元素的原生事件
// app-header.vue
<el-dropdown-itemdivided@click.native="handleLogout"
>退出</el-dropdown-item>
点击退出后,清除store内部的用户信息,同时跳转回到登录页面
- 通过mutation中的setUser清空user,由于setUser也设置了本地存储,这个时候也会自动清空
// 退出功能handleLogout () {// 清除store的用户信息this.$store.commit('setUser', null)// 跳转登录页this.$router.push('/login')}
可以通过Element的MessageBox弹框组件进行退出确认提示,增强体验感
- 确认提示使用&confirm(),确定触发then(),取消触发catch()
- 按钮内容默认为确定和取消,如不更改可以删除
在点击退出按钮时,通过this,$confirm()
进行确认消息设置 - 将前面设置的退出功能移步到位确认退出的代码区域
// 退出功能handleLogout () {this.$confirm('确认退出吗?', '退出提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {// 清除store的用户信息this.$store.commit('setUser', null)// 跳转登录页this.$router.push('/login')this.$message({type: 'success',message: '退出成功!'})}).catch(() => {this.$message({type: 'info',message: '取消退出'})})}
以上,登录功能到认证到退出一个闭环功能完成了,完善程度很高
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务

喜欢的朋友记得点赞、收藏、关注哦!!!