{"id":18880517,"url":"https://github.com/onewaytech/vue-auth-solution","last_synced_at":"2025-08-21T20:32:11.874Z","repository":{"id":143812450,"uuid":"110664608","full_name":"OneWayTech/Vue-Auth-Solution","owner":"OneWayTech","description":"Vue 权限管理解决方案","archived":false,"fork":false,"pushed_at":"2017-12-06T03:01:16.000Z","size":19,"stargazers_count":207,"open_issues_count":0,"forks_count":35,"subscribers_count":14,"default_branch":"master","last_synced_at":"2024-12-11T09:41:32.244Z","etag":null,"topics":["auth","management","route","vue"],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OneWayTech.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-11-14T08:51:24.000Z","updated_at":"2024-04-11T05:12:15.000Z","dependencies_parsed_at":null,"dependency_job_id":"35dfb9d0-7e9c-4d27-b495-58bdc9d9f869","html_url":"https://github.com/OneWayTech/Vue-Auth-Solution","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OneWayTech%2FVue-Auth-Solution","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OneWayTech%2FVue-Auth-Solution/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OneWayTech%2FVue-Auth-Solution/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OneWayTech%2FVue-Auth-Solution/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OneWayTech","download_url":"https://codeload.github.com/OneWayTech/Vue-Auth-Solution/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230532443,"owners_count":18240792,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["auth","management","route","vue"],"created_at":"2024-11-08T06:44:17.109Z","updated_at":"2024-12-20T04:07:08.637Z","avatar_url":"https://github.com/OneWayTech.png","language":null,"readme":"# § Vue 权限控制\n\n\u003e 在看本文档之前，您需要阅读 [Vue 另类状态管理](https://github.com/kenberkeley/vue-state-management-alternative)\n\n业界一向认为，权限只能是后端做  \n但如果在前后端分离的前提下仍是这样实现，那么前后端分离是没有任何意义的，还不如直接后端渲染实在\n\n目前有关 Vue 的权限控制并没有一个相对主流的解决方案，故在此抛砖引玉\n\n首先先说明，我司并没有用 Vuex，仅仅就是 Vue + Vue Router  \n稍微复杂一点的业务场景，都可以使用上述的“另类状态管理”以及 [eventbus](https://cn.vuejs.org/v2/guide/components.html#非父子组件的通信) 去解决 \n\n***\n\n## ⊙ 概览\n\n一般我们项目的源码目录 `src/` 下都会有一个 `mixins/`，里面必然存在一个 `session.js`，有如：\n\n\u003e 我司约定 mixin 中的变量以及函数都应当使用 `$` 结尾（为什么不能用在开头？因为 Vue 不会代理 `$` / `_` 开头的变量，故统一置尾）\n\n```js\n/*** src/mixins/session.js ***/\nimport authService from '@/services/authService' // 权限相关的 API 封装服务\nconst goToSSO = () =\u003e {\n  location.replace(`\u003c我司单点登录 URL\u003e?returnUrl=${encodeURIComponent(location.href)}`)\n}\n\n/* 登录凭据 */\nexport const session$ = {\n  id: null,\n  username: '',\n  role: '',\n  isLeader: null\n}\n\n/* 是否管理员 */\nexport const isAdmin$ = () =\u003e {\n  return session$.role === 'admin'\n}\n\n/* 是否销售主管 */\nexport const isSalesLeader$ = () =\u003e {\n  return session$.role === 'sales' \u0026\u0026 session$.isLeader\n}\n\n/* 挂载 DOM 前调用本函数 */\nexport const syncSession$ = () =\u003e {\n  return authService.getSession().then(sess =\u003e {\n    Object.assign(session$, sess) // 这里不建议写成 session$ = sess\n  }).catch(() =\u003e {\n    goToSSO() // 跳转到单点登录\n    throw new Error('Redirecting to SSO') // 继续抛出，避免之后的 then 执行挂载 DOM\n  })\n}\n\n// @export.default \u003cmixin\u003e\nexport default {\n  data: () =\u003e ({\n    session$\n  }),\n  computed: {\n    isAdmin$,\n    isSalesLeader$\n  },\n  methods: {\n    logout$ () {\n      authService.logout().then(goToSSO)\n    }\n  }\n}\n``` \n\n在启动文件中一般是这样的：\n\n```js\n/*** src/app.js ***/\nimport 'babel-polyfill'\nimport Vue from 'vue'\nimport App from '@/components/App'\nimport { syncSession$ } from '@/mixins/session'\n\n// 同步 session 后才挂载 DOM\nsyncSession$().then(() =\u003e {\n  /* eslint-disable no-new */\n  new Vue({\n    el: '#app',\n    router: require('@/routes/').default, // 路由涉及权限，因此须在同步 session 后才执行\n    render: h =\u003e h(App)\n  })\n})\n```\n\n以上就是我司权限管理的基石\n\n***\n\n## ⊙ 常见的疑问\n\nQ：为什么不使用 LocalStorage / SessionStorage / cookies 去保存登录凭据？这样可以全局访问很方便啊  \nA：可被篡改，没有安全性可言，而且还得考虑其过期时间以及解析错误等一系列不必要的麻烦\n\nQ：用 mixin 全局共享状态的好处是什么？  \nA：首先必须指出，要想让 `session$` 变成响应式，您必须要把 `src/mixins/session.js` 引入到任一组件中，这样就可以在组件内部（包括模板）访问到所有的变量与方法。而且，您还可以在非组件内部中访问。虽然 `export default` 的是 mixin 的固定格式，但 `export` 的却是直接的变量或方法，因此可以直接 `import { session$  } from '@/mixins/session'`，这几乎就像全局变量般便利，但又可以最大程度地保证安全性。更重要的，您还可以享受 Vue 带来的全局响应式、计算属性等一系列特性，而不仅仅是一个无法被外界篡改的闭包变量。举例说明：\n\n```js\nimport session from '@/mixins/session'\n\nexport default {\n  mixins: [session],\n  watch: {\n    /* 需求：测试模式下允许即时修改用户角色，请在控制台显示 */\n    'session$.role' (newRole, oldRole) {\n      console.info('角色已切换：', oldRole, ' =\u003e ', newRole)\n    }\n  }\n}\n```\n\nQ：路由控制怎么处理？  \nA：下面我们接着说\n\n***\n\n## ⊙ 路由级别控制\n\n能在 Vue 层面上解决的事情没必要动用到 Vue Router 的特性，否则权限就写得太散了  \n业内主流的方式都是通过 `beforeEach` 来获取 `meta` 信息以拦截  \n不过话说回来，既然你都不想该角色看到的路由，为什么你还要挂载？画蛇添足多此一举莫过于此\n\n举个例子，一个项目有 `/a`（默认）、`/b`、`/c`、`/d`、`/e` 五个路由，需满足：  \n* 只有 管理员 可以看到 `/e`  \n* 只有 管理员 与 销售 Leader 可以看到 `/d`\n\n那么路由的定义可以这样写：\n\n```js\nimport { isAdmin$, isSalesLeader$ } from '@/mixins/session'\n\nexport default [\n  {\n    path: '/a',\n    alias: '/',\n    component: require('@/views/a/')\n  },\n\n  {\n    path: '/b',\n    component: require('@/views/b/')\n  },\n\n  {\n    path: '/c',\n    component: require('@/views/c/')\n  },\n\n  (isAdmin$() || isSalesLeader$()) \u0026\u0026 {\n    path: '/d',\n    component: require('@/views/d/')\n  },\n\n  isAdmin$() \u0026\u0026 {\n    path: '/e',\n    component: require('@/views/e/')\n  },\n  \n  { // 404 置尾\n    path: '*',\n    component: {\n      beforeCreate () {\n        this.$router.replace('/')\n      },\n      render: h =\u003e null\n    }\n  }\n].filter(route =\u003e route) // 排除掉为 false 的项\n```\n\n这下你能可以理解为什么在启动文件中要在 `syncSession$` 完成后才引入路由了吧？  \n如果在开头就 `import routes from '@/routes/'` 则无法实现控权（因为是先执行）\n\n***\n\n## ⊙ 按钮级别控制\n\n基本就是把 `@/mixins/session` 引入到组件中就可以了，没有任何难度\n\n***\n***\n\n### 2017/12/6 针对 SegmentFault 下[评论](https://segmentfault.com/p/1210000012206425?_ea=2945405)的更新\n\n* 针对 `没有用动态路由，导致用户登录前不能初始化Vue应用，所以登陆页只能单独做，开始我也是这么做的，但始终觉得url跳转的体验不好，所以用动态路由解决了` 的解决方案：如果您的公司没有 SSO，那么每个项目都只能重复造轮子做登录页（之前我司就是如此），此时只能借助 `vue-router` 的钩子函数 `beforeEach` 控权：\n\n```js\nimport { isLogin$ } from '@/mixins/session'\nconst LOGIN_PATH = '/auth/login'\n\nexport default function authInterceptor(to, from, next) {\n  if (isLogin$()) {\n    switch (to.path) {\n      case LOGIN_PATH:\n        next('/')\n        return\n      default:\n        next()\n    }\n  } else {\n    switch (to.path) {\n      case LOGIN_PATH:\n        next()\n        return\n      default:\n        next(`${LOGIN_PATH}?referrer=${encodeURIComponent(to.fullPath)}`)\n    }\n  }\n}\n\n// 使用方式：router.beforeEach(authInterceptor)\n```\n\n* 针对 `在前端路由文件中根据角色做判断的做法不够灵活，路由权限还是由后端分发给前端比较好，这样当需要修改角色权限时，后端改一下配置，前端刷新就生效了` 的回应：把我司现行完善的权限设计一股脑搬出来说没有意义，以上例子只是为了简要说明，更重要的是思想\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fonewaytech%2Fvue-auth-solution","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fonewaytech%2Fvue-auth-solution","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fonewaytech%2Fvue-auth-solution/lists"}