在本例子中,使用的请求方法都是使用axios,vuejs的官方推荐。
安装与使用axios
在main.js(如果你使用的是vue-cli脚手架搭建的项目)中引入
1 2
| import axios from 'axios' Vue.prototype.$http = axios
|
之后使用小栗子即是
1 2 3 4 5
| this.$http.get(URL).then(response => { }, response => { })
|
登录
在登录部分,登录成功后,将服务器返回的token存在sessionstorage中,当然也可以放在localstorage,或者使用vuex的方式存储token数据和用户信息。
1 2 3 4 5 6 7 8 9 10 11 12
| var url = "/api/web/login"; var data={username:this.username,password:this.password}; this.$http({method:"get",url:url,auth:data}).then(response => { if(response.status==200){ sessionStorage.token = response.data.token; sessionStorage.username = response.data.username; this.$router.push(this.$route.query.redirect||'/'); //登录成功则跳转到首页 } }).catch(function(error){ });
|
路由配置
以前写路由配置的时候即只是如下示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import Vue from 'vue' import Router from 'vue-router' import Web from '@/components/Web' import Dashboard from '@/components/Dashboard'
Vue.use(Router)
export default new Router({ routes: [ { path: '/', name: 'Web', component: Web, children:[ { path: "/", name: 'Dashboard', component: Dashboard } ] } ] })
|
当打包构建应用时,Javascript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。这就是懒加载。
改成如下的方式即可实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| import Vue from 'vue' import Router from 'vue-router'
Vue.use(Router) const router = new Router({ routes: [ { path: '/', name: 'Web', component(resolve) { require.ensure(['@/components/Web.vue'], () => { resolve(require('@/components/Web.vue')); }); }, meta: { requireAuth: true }, children:[ { path:"/", name:'Dashboard', component(resolve) { require.ensure(['@/components/Dashboard.vue'], () => { resolve(require('@/components/Dashboard.vue')); }); }, meta: { requireAuth: true } }, { path:'/login', name:'Login', component(resolve) { require.ensure(['@/components/Login.vue'], () => { resolve(require('@/components/Login.vue')); }); }, } ] })
|
同时,还加入了路由元信息,配置了meta-requireAuth字段,来判断在这个路由中是否需要身份认证
利用"导航守卫“来检查该字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| router.beforeEach((to, from, next) => { let token = sessionStorage.token; if(to.meta.requireAuth) { if(token) { next() } else { next({ path: '/login', query: { redirect: to.fullPath } }) } } else { next() } })
export default router
|
[tip:]
每个守卫方法接收三个参数:
- to: Route: 即将要进入的目标 路由对象
- from: Route: 当前导航正要离开的路由
- next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
- next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
- next(false): 中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
- next(’/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。
- next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
axios拦截器
在做了以上情况之后,你只是做到了表面上的前端路由控制,接下来我们使用axios拦截器实现以下功能:
1.登陆后,为了保持认证的请求,在每次请求的头部加上登录成功时返回的token信息
2.发送不合法的请求或者在没有认证的情况下发送请求后,如果服务器返回的状态是401(Unauthorized 未授权)或者403(Forbidden 拒绝),则跳转到登录页面。
发起每次请求时,都在头部加上token字段,让服务器验证token是否有效,如果后端接口返回 401 或者403 ,则让用户重新登录,并清除token信息。
1 2 3 4 5 6 7 8 9 10 11
| axios.interceptors.request.use( config=>{ if(sessionStorage.token){ config.auth = {username:sessionStorage.token,password:""}; } return config; }, err=>{ return Promise.reject(err); } );
|
在这里,我是将token信息作为username,password置为空,赋予axios的auth字段,这样会直接进行base64编码作为基础认证传给服务器。
当然也可以使用以下方式将token赋予请求头部的Authorization字段返回给服务器。
1
| config.headers.Authorization = `token ${sessionStoreage.token}`;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| axios.interceptors.response.use( response => { return response; }, error => { if (error.response) { switch (error.response.status) { case 401: sessionStorage.token=""; router.replace({ path: '/login', query: {redirect: router.currentRoute.fullPath} }); break; case 403: sessionStorage.token=""; router.replace({ path: '/login', query: {redirect: router.currentRoute.fullPath} }) break; } } return Promise.reject(error.response.data) } );
|
至此,整个用户认证实例即结束。
登出的时候,只要将保存在storage里的token和用户信息清除,跳转到登录页或者不需认证的首页即可。
参考:
https://router.vuejs.org/zh-cn/advanced/navigation-guards.html