路由守卫、网络请求、用户信息、角色路由、微信登录

243 阅读1分钟

Vue项目中,路由守卫用于在导航之前进行权限检查。通过beforeEachafterEach钩子函数,可实现如身份验证和页面加载状态管理。

网络请求通常使用axios库,通过封装API模块来管理HTTP请求和响应。

全局用户信息可以通过Vuex进行管理,Vuex是Vue.js的状态管理模式,能够在整个应用中共享和管理用户状态,包括登录信息、权限和用户偏好。

  1. 路由守卫:

    // src/main.js
    
    import Vue from "vue";
    import App from "./App";
    import store from "./store";
    import router from "./router";
    import { initRouterController } from "./router/globalController";
    
    const renderAppHandle = async () => {
      // 初始化用户登录状态
      await store.dispatch("initAuthriozation");
    
      // 注册全局路由
      initRouterController({ router, store });
    
      new Vue({
        router,
        store,
        render: (h) => h(App),
      }).$mount("#app");
    };
    
    renderAppHandle();
    
    // src/router/globalController.js
    
    import { services } from "@/service";
    import reporter from "@/lib/reporter";
    import qs from "qs";
    
    export function initRouterController({ router, store }) {
      // 判断是否有无登录
      router.beforeEach((to, from, next) => {
        const { noAuth = false, title = "" } = to.meta;
    
        window.document.title = title;
    
        const token = window.localStorage.getItem("token");
    
        if (noAuth) {
          next();
          return;
        }
    
        if (token) {
          next();
        } else {
          next({ name: "login" });
        }
      });
    
      router.afterEach((to, from) => {});
    }
    
    // src/router/index.js
    
    import Vue from "vue";
    import VueRouter from "vue-router";
    
    Vue.use(VueRouter);
    
    const router = new VueRouter({
      routes: [
        {
          path: "/",
          component: () => import("@/views/login/index"),
        },
        {
          path: "/login",
          name: "login",
          component: () => import("@/views/login/index"),
          meta: { noAuth: true, title: "登录" },
        },
      ],
    });
    
    export default router;
    
  2. 网络请求

    // src/lib/request.js
    
    import axios from "axios";
    import store from "@/store";
    import qs from "qs";
    
    // 可以设置 BaseURL 做开发代理
    const request = axios.create({
      headers: {
        post: {
          "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
        },
        "X-Requested-With": "XMLHttpRequest",
      },
      timeout: 60 * 1000, // 设置一分钟过期
      responseType: "json", 
    });
    
    // 请求发送前拦截器
    request.interceptors.request.use(
      (config) => {
        const token = store.getters.token;
        if (token) {
          config.headers.Authorization = token;
        }
    
        if (config.method === "get") {
          config.paramsSerializer = function (params) {
            return qs.stringify(params, { arrayFormat: "repeat" });
          };
        }
    
        return config;
      },
      (err) => {
        console.error(err);
      }
    );
    
    // 请求返回时处理
    request.interceptors.response.use(
      (res) => {
        const resData = res.data;
    
        if (["blob", "arraybuffer"].includes(res.config.responseType)) {
          return resData;
        }
    
        const {
          data,
          errCode,
          statusCode = 404,
          message = "数据请求失败",
        } = resData || {};
    
        // 在这里做一些对响应码判断,并做相应处理逻辑
      },
      (err) => {
        if (err.response) {
          // err.response.status;
          // err.response;
        } else {
          // "non-response";
        }
        return Promise.reject(err);
      }
    );
    
    export default request;
    
  3. 全局用户信息

    // src/store/modules/authriozation.js
    import service from "@/service";
    import router from "@/router";
    
    export default {
      state: {
        token: "",
        userInfo: null,
      },
      mutations: {
        SET_TOKEN(state, token) {
          state.token = token;
        },
        SET_USERINFO(state, userInfo) {
          state.userInfo = userInfo;
        },
      },
      actions: {
        // 初始化
        async initAuthriozation({ commit, getters, dispatch }) {
          const token = window.localStorage.getItem('token');  
          const userInfo = (() => {
            let str = window.localStorage.getItem('userInfo');
            let res
            try {
              res = JSON.parse(str)
            } catch (error) {
              res = null
            }
    
            return res;
          })();  
    
          if(token) {
            commit('SET_TOKEN', token);
          } else {
            dispatch('logout');
            return;
          }
    
          if(userInfo) {
            commit('SET_TOKEN', token);
          } else {
            dispatch('getUserInfo');
          }
        },
        // 登录
        async login({ commit }, { username, password }) {
          let token;
          try {
            // src/service/authriozation.js: login 登录
            token = await service.authriozation.login({ username, password });
          } catch {
            console.error("登录失败");
            return;
          }
    
          commit("SET_TOKEN", token);
          window.localStorage.setItem("token", token);
        },
        // 获取用户信息
        async getUserInfo({ commit }) {
          let userInfo;
          try {
            // src/service/authriozation.js: getUserInfo 获取用户信息
            userInfo = await service.authriozation.getUserInfo();
          } catch {
            console.error("获取用户信息失败");
            return;
          }
    
          commit("SET_USERINFO", userInfo);
          window.localStorage.setItem("userInfo", JSON.stringify(userInfo));
        },
        // 退出登录
        logout({ commit }) {
          commit("SET_TOKEN", "");
          commit("SET_USERINFO", null);
          window.localStorage.removeItem("token");
          window.localStorage.removeItem("userInfo");
    
          // src/service/authriozation.js: logout 退出登录
          service.authriozation.logout();
          router.push({ name: 'login' });
        },
      },
      getters: {
        token: (state) => state.token,
        userInfo: (state) => state.userInfo,
      },
    };
    
  4. 角色路由

    不同角色的权限路由不同,一般在登录后获取,并添加路由。

    import router from '@/router';
    
    // 假设请求返回的是类似如下路由数据
    const roleRoutes = [
      {
        path: '/about',
        name 'about',
        component: 'pages/about/index.vue'
      }
    ];
    
    // 添加到VueRouter
    roleRoutes.forEach(roleRoute => {
      roleRoute.component = () => import(`@/${roleRoute.component}`);
      router.addRoute(roleRoute);
    });
    
  5. 微信登录(后端处理)

    • 先获取 token
      axios
        .get(`https://api.weixin.qq.com/cgi-bin/token`, {
          params: {
            grant_type: "client_credential",
            // 微信公众平台 - 设置 - 开发设置
            appid: "AppID",
            secret: "AppSecret",
          },
        })
        .then(({ expires_in, access_token }) => {
          // access_token: 获取到的凭证
          // expires_in: 凭证有效时间,单位:秒。目前是7200秒之内的值。
        });
      
    • 生成登录二维码
      axios
        .get(`https://api.weixin.qq.com/cgi-bin/qrcode/create`, {
          params: {
            access_token: "access_token",
          },
        })
        .then(({ key, ticket }) => {
          // ticket: 用于获取登录二维码
          // key: 用于检验登录状态
        });
      
      // 二维码图片链接
      function getQrcode() {
        if (!ticket) return;
      
        const qrcode = `https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=${ticket}`;
      
        // 检验登录状态
        // 超过一分钟未扫码登录重新生成二维码
        let time = 60;
        const intervalId = setInterval(() => {
          checkoutLogin(intervalId, --time);
        }, 1000);
      }
      
    • 检验二维码是否过期
      // 检验登录状态
      function checkoutLogin(intervalId, time) {
        // 超过一分钟未扫码登录重新生成二维码
        if (0 === time) {
          clearInterval(intervalId);
          getQrcode();
        }
      
        // 调接口判断是否已经登录
        // 如果已经登录 clearInterval(intervalId); window.localStorage.setItem('token', token);
      }