Vue前端鉴权方案,前后端分离

更新日期: 2020-01-15阅读: 2.2k标签: 方案

技术

前端vue全家桶,后台.net。


需求分析

  1. 前端路由鉴权,屏蔽地址栏入侵
  2. 路由数据由后台管理,前端只按固定规则异步加载路由
  3. 权限控制精确到每一个按钮
  4. 自动更新token
  5. 同一个浏览器只能登录一个账号


前端方案

对于需求1、2、3,采用异步加载路由方案
  1. 首先编写vue全局路由守卫
  2. 排除登录路由和无需鉴权路由
  3. 登录后请求拉取用户菜单数据
  4. 在vuex里处理菜单和路由匹配数据
  5. 将在vuex里处理好的路由数据通过addRoutes异步推入路由
  router.beforeEach((to, from, next) => {
    // 判断当前用户是否已拉取权限菜单
    if (store.state.sidebar.userRouter.length === 0) {
      // 无菜单时拉取
      getMenuRouter()
        .then(res => {
          let _menu = res.data.Data.ColumnDataList || [];
          // if (res.data.Data.ColumnDataList.length > 0) {
          // 整理菜单&路由数据
          store.commit("setMenuRouter", _menu);
          // 推入权限路由列表
          router.addRoutes(store.state.sidebar.userRouter);
          next({...to, replace: true });
          // }
        })
        .catch(err => {
          // console.log(err);
          // Message.error("服务器连接失败");
        });
    } else {
      //当有用户权限的时候,说明所有可访问路由已生成 如访问没权限的菜单会自动进入404页面
      if (to.path == "/login") {
        next({
          name: "index"
        });
      } else {
        next();
      }
    }
  } else {
    // 无登录状态时重定向至登录 或可进入无需登录状态路径
    if (to.path == "/login" || to.meta.auth === 0) {
      next();
    } else {
      next({
        path: "/login"
      });
    }
  }
});
注意
我这里无需鉴权的路由直接写在router文件夹下的index.js,通过路由元信息meta携带指定标识
  {
    path: "/err-404",
    name: "err404",
    meta: {
       authentication: false
    },
    component: resolve => require(["../views/error/404.vue"], resolve)
  },
上面说到路由是根据后台返回菜单数据根据一定规则生成,因此一些不是菜单,又需要登录状态的路由,我写在router文件夹下的router.js里,在上面步骤4里处理后台返回菜单数据时,和处理好的菜单路由数据合并一同通过addRoutes推入。 这样做会有一定的被地址栏入侵的风险,但是笔者这里大多是不太重要的路由,如果你要求咳咳,可以定一份字典来和后台接口配合精确加载每一个路由。
// 加入企业
{
  path: "/join-company",
  name: "join-company",
  component: resolve => require([`@/views/index/join-company.vue`], resolve) 
},
在vuex中将分配的菜单数据转化为前端可用的路由数据,我是这样做的: 管理系统在新增菜单时需要填写一个页面地址字段Url,前端得到后台菜单数据后根据Url字段来匹配路由加载的文件路径,每个菜单一个文件夹的好处是:你可以在这里拆分js、css和此菜单私有组件等
    menu.forEach(item => {
          let routerItem = {
            path: item.Url,
            name: item.Id,
            meta: {
              auth: item.Children,
            }, // 路由元信息 定义路由时即可携带的参数,可用来管理每个路由的按钮操作权限
            component: resolve =>
              require([`@/views${item.Url}/index.vue`], resolve) // 路由映射真实视图路径
          };
          routerBox.push(routerItem);
      });
关于如何精确控制每一个按钮我是这样做的,将按钮编码放在路由元信息里,在当前路由下匹配来控制页面上的按钮是否创建。 菜单数据返回的都是多级结构,每个菜单下的子集就是当前菜单下的按钮权限码数组,我把每个菜单下的按钮放在此菜单的路由元信息meta.auth中。这样作的好处是:按钮权限校验只需匹配每个菜单路由元信息下的数据,这样校验池长度通常不会超过5个。
created() {
  this.owner = this.$route.meta.auth.map(item => item.Code);
}
methods: {
    matchingOwner(auth) {
      return this.owner.some(item => item === auth);
    }
}
    
需求4自动更新token,就是简单的时间判断,并在请求头添加字段来通知后台更新token并在头部返回,前端接受到带token的请求就直接更新token
// 在axios的请求拦截器中
    let token = getSession(auth_code);
    if (token) config.headers.auth = token;
    if (tokenIsExpire(token)) {
      // 判断是否需要刷新jwt
      config.headers.refreshtoken = true;
    }
// 在axios的响应拦截器中
  if (res.headers.auth) {
    setSession(auth_code, res.headers.auth);
  }
对于需求5的处理比较麻烦,要跨tab页只能通过cookie或local,笔者这里不允许使用cookie因此采用的localstorage。通过打开的新页面读取localstorage内的token数据来同步多个页面的账号信息。token使用的jwt并前端md5加密。 这里需要注意一点是页面切换要立即同步账号信息。
经过需求5改造后的全局路由守卫是这样的:
function _AUTH_() {
// 切换窗口时校验账号是否发生变化
window.addEventListener("visibilitychange", function() {
  let Local_auth = getLocal(auth_code, true);
  let Session_auth = getSession(auth_code);
  if (document.hidden == false && Local_auth && Local_auth != Session_auth) {
    setSession(auth_code, Local_auth, true);
    router.go(0)
  }
})

router.beforeEach((to, from, next) => {
    // 判断当前用户是否已拉取权限菜单
    if (store.state.sidebar.userRouter.length === 0) {
      // 无菜单时拉取
      getMenuRouter()
        .then(res => {
          let _menu = res.data.Data.ColumnDataList || [];
          // if (res.data.Data.ColumnDataList.length > 0) {
          // 整理菜单&路由数据
          store.commit("setMenuRouter", _menu);
          // 推入权限路由列表
          router.addRoutes(store.state.sidebar.userRouter);
          next({...to, replace: true });
          // }
        })
        .catch(err => {
          // console.log(err);
          // Message.error("服务器连接失败");
        });
    } else {
      //当有用户权限的时候,说明所有可访问路由已生成 如访问没权限的菜单会自动进入404页面
      if (to.path == "/login") {
        next({
          name: "index"
        });
      } else {
        next();
      }
    }
  } else {
    // 无登录状态时重定向至登录 或可进入无需登录状态路径
    if (to.path == "/login" || to.meta.auth === 0) {
      next();
    } else {
      next({
        path: "/login"
      });
    }
  }
});
}
经过需求5改造后的axios的请求拦截器是这样的,因为ie无法使用visibilitychange,并且尝试百度其他属性无效,因此在请求发出前做了粗暴处理:
if (ie浏览器) { 
    setLocal('_ie', Math.random())
    let Local_auth = getLocal(auth_code, true);
    let Session_auth = getSession(auth_code);
    if (Local_auth && Local_auth != Session_auth) {
      setSession(auth_code, Local_auth, true);
      router.go(0)
      return false
    }
  }
这里有一个小问题需要注意:因为用的local因此首次打开浏览器可能会有登录已过期的提示,这里相信大家都能找到适合自己的处理方案


结语

经过这些简单又好用的处理,一个基本满足需求的前后端分离前端鉴权方案就诞生啦

链接: https://fly63.com/article/detial/7260

preact_一款React的3kb轻量化方案

是react的3kb轻量化方案,拥有同样的 ES6 API,Preact 在 DOM上实现一个可能是最薄的一层虚拟 DOM 实现。

URI不规范编码解决方案

RFC 7230 与 RFC 3986 定义了 HTTP/1.1 标准并对 URI 的编解码问题作出了规范。但是,文本形式的规范和最终落地的标准之间总是存在着差距。标准中共 82 个字符无需编码。

图片降级方案原来这么简单?

在做项目优化的时候,发现页面加载很慢。结果一看主要的问题就是就是图片的大小过慢,然后呢准备呢去做优化, 本来想去用webp,去优化的,但是呢这个图片是不是我们就用不了呢,然后看了下业界优化王

es6模块加载方案

本篇我们重点介绍以下四种模块加载规范: AMD CMD CommonJS ES6 模块 最后再延伸讲下 Babel 的编译和 webpack 的打包原理。

Github访问速度慢的解决方案总汇

GitHub的CDN(Content Delivery Network,即内容分发网络)域名遭到DNS污染,无法连接使用GitHub的加速分发服务器,所以国内访问速度较慢。

开发人员犯的五大 JavaScript 错误及其解决方案

JavaScript 语言有着悠久的历史。有很多开发人员仍在学习基础知识。但是,如果您正在尝试学习该语言并迈出第一步,您需要知道新开发人员会犯什么错误。您已经研究过 JavaScript 开发教程,并且知道它是世界上最流行的语言之一。

微信中H5页面唤起 APP方案_直接打开appStore

H5网页在微信上是无法直接打开app链接的,需要使用微信开放标签wx-open-launch-app,它主要用于微信H5网页唤醒app,该标签只有在微信中才会显示。

前端加载超大图片(100M以上)实现秒开解决方案

而对于几百M或上G的大图而言,不管对图片进行怎么优化或加速处理,要实现秒开也是不太可能的事情。而上面介绍的第二条“图像分割切片”是最佳解决方案。下面介绍下如何对大图进行分割

现代 CSS 解决方案:原生嵌套(Nesting)

CSS 原生嵌套还处于工作草案 Working Draft (WD) 阶段,而今天(2023-09-02),CSS 原生嵌套 Nesting 终于成为了既定的规范!在之前,只有在 LESS、SASS 等预处理器中,我们才能使用嵌套的写法

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!