1. 路由拦截

vue项目中通常都是通过配置routes配置项来控制路由跳转,例如设置

  1. routes:[
  2. {
  3. path:'cinema',
  4. redirect:'/page/cinema',
  5. component: BlankLayout,
  6. meta:{title:'影院'requiresAuth:true},
  7. children:[
  8. {
  9. path:'cinema/plan' ,
  10. name:'cinmeapPlan',
  11. component:()=>import('./view/cinema/plan')
  12. meta:{title:'影院排期'}
  13. },
  14. {
  15. path:'cinema/cinemaDetail' ,
  16. name:'cinemaDetail',
  17. component:()=>import('./view/cinema/cinemaDetail')
  18. meta:{title:'影院详情'}
  19. },
  20. ]
  21. }]

利用routes中的meta属性添加一个字段,用作标识,首先在router.js的文件中定义router,具体如下:

  1. import Vue from 'vue'
  2. import VueRouter from 'vue-router'
  3. Vue.use(VueRouter)
  4. const router = new VueRouter({ routes })
  5. export router

接着在下面文件中结合路由守卫,进行登陆验证,另外如果用户登录成功之后,token会默认放在vuex中的getters中,所以在导航守卫中判断对应getters是否存在,如果存在,证明用户已登录,允许用户进入该路由。否则就跳转登陆页,并把当前页的路由座位query参数传递给login页面:

  1. to.meta && (typeof to.meta.title !== 'undefined' && setDocumentTitle(`${to.meta.title}`))
  2. if (to.matched.some(record => record.meta.requiresAuth)) {
  3. // this route requires auth, check if logged in
  4. // if not, redirect to login page.
  5. if (!store.getters.token) {
  6. next({
  7. path: '/login',
  8. query: { redirect: to.fullPath }
  9. })
  10. } else {
  11. if (to.query.siteCode) {
  12. next()
  13. return
  14. }
  15. if (from.query.siteCode) {
  16. const query = JSON.parse(JSON.stringify(to.query))
  17. query.siteCode = from.query.siteCode
  18. next({
  19. path: to.path,
  20. query: query
  21. })
  22. } else {
  23. next() // 确保一定要调用 next()
  24. }
  25. }
  26.  }

这里为什么要遍历to.matched数组判断meta的requiresAuth字段,而不是直接使用使用to.matched.requiresAuth来判断,首先例子中给的是cinema,一级路由设置了requiresAuth,而cinemaPlan没有设置。假设有两种情况:

前提:vue路由匹配时会同时匹配满足情况的所有路由,即如果路由是/cineme/plan的话,/cinema也会触发。另外如果较高等级的路由需要控制的话,它所有的嵌套路由基本都是需要控制的。

  1. cinema具有登陆控制,cinemaPlan没有,如果用户点击路由跳转的话,它必然是先进入一级路由,再去二级路由,一级路由实现登陆控制,利用to.meta是能够满足的。注意这里是正常点击,但是如果用户通过改变url的话去访问cinemaPlan的话,则需要给cinemaPlan路由添加requiresAuth字段,同理也需要给requiresAuth添加字段,如果路由比较多的话,就很麻烦
  2. cinema没有登陆控制,而cinemaPlan有,这种情况确实不怕用户改变url访问二级路由了,但是二级路由过多,也是需要设置许多requiresAuth。

所以,为了方便,直接遍历to.matched数组,该数组保存着匹配到的所有路由信息。就该例而言,访问cinema时,matched数组长度为1,访问cinemaplan时,matched数组长度为2,即保存着/cinema,以及/cinema/plan,其实啰嗦了这么直接使用to.meta字段判断也可以,就是需要给所有需要控制路由添加requiresAuth,to.matched则只需要给较高的一级添加requiresAuth,其下所有的子路由都不必添加

2.axios拦截器

当前token失效了,但是token依然保存在本地,这时候你去访问需要权限的路由时,实际上应该让用户重新登陆。这时候就需要结合http拦截器+后端接口返回的http状态码来判断

  1. // http request拦截器
  2. axios.interceptors.request.use(
  3. config =>{
  4. if(store.state.token){ //判断是否存在token,如果存在的话,则每个http header都加上token
  5. config.header.Authorization = `token ${store.state.token}`;
  6. }
  7. return config
  8. },
  9. err=>{
  10. return Promise.reject(err);
  11. }
  12. )
  13. // http reponse拦截器
  14. axios.interceptors.response.use(response=>{
  15. return response,
  16. },
  17. error=>{
  18. if(error.reponse){
  19. switch(error.reponse.status){
  20. case 401:
  21. store.commit(types.LOGOUT)
  22. router.currentRoute.name !== 'login' && router.replace({
  23. path: 'login',
  24. query: {redirect: router.currentRoute.fullPath}
  25. }
  26. }
  27. return promise.reject(error.reponse.data);
  28. }
  29. )