• Vue 项目中用户登录及 token 验证的思路


    vue-router 导航守卫
    所谓“导航”,即路由正在发生变化。可在路由跳转时完成一些操作,而 router.beforeEach() 全局前置守卫可以在路由跳转前对现在状态进行校验,例如验证用户的登录状态,若未登录则可以有效进行拦截。

    const router = new VueRouter({ ... })
    
    router.beforeEach((to, from, next) => {
      // ...
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    axios 拦截器
    axios 拦截器又分为请求拦截器与响应拦截器,可在请求或响应被 then 或 catch 处理前拦截它们,即在前端页面向后端发送请求时触发进行拦截。

    // 添加请求拦截器
    axios.interceptors.request.use(function (config) {
    	// 在发送请求之前做些什么
        return config;
    }, function (error) {
        // 对请求错误做些什么
        return Promise.reject(error);
    });
    
    // 添加响应拦截器
    axios.interceptors.response.use(function (response) {
        // 2xx 范围内的状态码都会触发该函数。
        // 对响应数据做点什么
        return response;
    }, function (error) {
        // 超出 2xx 范围的状态码都会触发该函数。
        // 对响应错误做点什么
        return Promise.reject(error);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    如果你稍后需要移除拦截器,可以这样:

    const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
    axios.interceptors.request.eject(myInterceptor);
    
    • 1
    • 2

    区别
    导航守卫更像是路由级拦截,而 axios 拦截器则是接口级拦截,这是它们本质上的区别。

    关于它们更详细的区分在后文会提及,这里先不讨论,因为知识点没有带入到实例场景中进行应用容易造成理解不深或理解不了的问题,先回归正文。

    Vue 项目实现 token 验证

    401 Unauthorized:该状态码表示用户所发送的请求没有访问权限,需要进行身份认证。返回含有 401 的响应必须包含一个适用于被请求资源的 WWW-Authenticate 首部用以质询用户信息。
    
    • 1

    前后端分离开发方式固然好,但也带来了一些棘手的问题,如跨域处理、token 验证…

    接下来展开说说 token 验证。

    在 Vue 项目中实现 token 验证的大致思路如下:

    第一次登录时,前端调用后端的登录接口,发送用户名和密码
    后端收到请求后,验证用户名和密码的合法性,成功则给前端返回一个 token
    前端拿到 token 后,将其存储到 localStorage 和 vuex 中,并跳转路由页面
    此后前端每次跳转路由时,就判断 localStorage 中有无 token,没有则跳转到登录页面,有则跳转到对应的路由页面
    且前端每次调用后端接口时,都要在请求头中携带 token
    后端判断请求头中有无 token
    header 中存在 token:验证 token 的合法性,成功则返回前端请求的数据;失败(如 token 过期)就返回 401 状态码(401 Unauthorized)
    header 中不存在 token:同样返回 401 状态码
    如果前端接收到 401 状态码,则清除 token 信息并跳转到登陆页面
    Login.vue

    <template>
      <div>
        <input type="text" v-model="loginForm.username" placeholder="用户名"/>
        <input type="text" v-model="loginForm.password" placeholder="密码"/>
        <button @click="login">登录</button>
      </div>
    </template>
     
    <script>
    import { mapMutations } from 'vuex';
    export default {
      data () {
        return {
          loginForm: {
            username: '',
            password: ''
          },
          userToken: ''
        };
      },
     
      methods: {
        ...mapMutations(['changeLogin']),
        login () {
          let _this = this;
          if (this.loginForm.username === '' || this.loginForm.password === '') {
            alert('账号或密码不能为空');
          } else {
            this.axios({
              method: 'post',
              url: '/user/login',
              data: _this.loginForm
            }).then(res => {
              console.log(res.data);
              _this.userToken = 'Bearer ' + res.data.data.body.token;
              // 将用户token保存到vuex中
              _this.changeLogin({ Authorization: _this.userToken });
              _this.$router.push('/home');
              alert('登录成功');
            }).catch(error => {
              alert('账号或密码错误');
              console.log(error);
            });
          }
        }
      }
    };
    </script>
    
    • 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
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    store/index.js

    import Vue from 'vue'
    import Vuex from 'vuex'
    Vue.use(Vuex)
     
    const store = new Vuex.Store({
      state: {
        // 存储token
        Authorization: localStorage.getItem('Authorization') ? localStorage.getItem('Authorization') : ''
      },
      mutations: {
        // 修改token,并将token存入localStorage
        changeLogin (state, user) {
          state.Authorization = user.Authorization;
          localStorage.setItem('Authorization', user.Authorization);
        }
      }
    });
     
    export default store
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    router/index.js: 导航守卫

    import Vue from 'vue';
    import Router from 'vue-router';
    import login from '@/components/login';
    import home from '@/components/home';
     
    Vue.use(Router);
     
    const router = new Router({
      routes: [
        {
          path: '/',
          redirect: '/login'
        },
        {
          path: '/login',
          name: 'login',
          component: login
        },
        {
          path: '/home',
          name: 'home',
          component: home
        }
      ]
    });
    
    // 导航守卫:使用 router.beforeEach 注册一个全局前置守卫,判断用户是否登陆
    router.beforeEach((to, from, next) => {
      if (to.path === '/login') {
        next();
      } else {
        let token = localStorage.getItem('Authorization');
     
        if (token === null || token === '') {
          next('/login');
        } else {
          next();
        }
      }
    });
    
    export default router
    
    • 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

    请求拦截器

    // 请求拦截器, 每次请求都会在请求头中携带token
    axios.interceptors.request.use((config) => {
      if(localStorage.getItem('Authorization')) {
        config.headers.Authorization = localStorage.getItem('Authorization')
      }
      return config;
    }, (error) => {
      return Promise.reject(error);
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    401 Unauthorized 处理
    如果前端拿到后端返回的 401 状态码,则清除 token 信息并跳转到登陆页面。

    localStorage.removeItem('Authorization');
    this.$router.push('/login');
    
    • 1
    • 2

    导航守卫 与 请求拦截器
    导航守卫仅仅简单判断是否有 token 值存在(不管该 token 是否有效),如果不存在/失效就进行重定向。

    请求拦截器是向后端发送请求并校验,如果 token 合法就访问成功,否则访问失败并进行重定向。

    ⭐总之,在进行页面权限判断时通常都是让 vue-router 导航守卫和 axios 拦截器搭配使用!

  • 相关阅读:
    Java面试题01
    [Unity]打包Android后xxx方法丢失。
    Mybatis核心配置文件中的标签介绍
    pytorch入门
    SpringBoot整合任务系统(quartz和SpringTask)
    Spring Boot | Spring Boot 实现 “Redis缓存管理“
    Android系统修改AOSP输入法默认输入语言
    【随笔】提高代码学习水平(以更高的视角看事物)
    【红队】ATT&CK - 自启动 - 注册表运行键、启动文件夹
    程序员挖“洞”致富:发现一个漏洞,获赏 1272 万元
  • 原文地址:https://blog.csdn.net/m0_49515138/article/details/127904182