注: 本文没具体具体,仅仅讨论的是理论上存在的实现方案。
前言
现在基于 前后端分离模式的前端工程是越来越多了,很多公司都逐渐接受、引入实践前后端分离的开发模式。不可否认,前后端分离的模式给予了前后端协作一个新的解决方案,而前端框架的百花齐放以及 Nodejs
的普及、踏入服务器开发的领域等等也为前后端分离引入了新鲜的玩法。但同时也给了前端新的挑战,前端工程化、基础建设等问题也都逐渐凸现出来;而在很多业务中的问题也都由服务器开发人员前移到了客户端开发人员(大部分指前端工程师)处理。
权限的定义
权限问题一直是一些企事业后台等项目必须面对的问题。以前的权限基本都是由后端开发人员设计维护并且处理在 view
上逻辑处理。 前后端分离开发之后势必就出现了权限系统分离的问题。由于权限的问题比较敏感,尤其是涉及金钱和隐私的数据时候,本质上都是不允许用户跨权限的访问;对于开发人员既要保证权限系统的健壮性、安全性,也要平衡好开发阶段、维护阶段的迭代诉求。
权限控制的粒度在每个项目中都是不一样的,但大体可以划分为 路由级别权限 和 元素级别权限 。
路由级别权限的权限很容易理解,是指基于 URL 的权限控制,比如 管理员用户可以访问 a.html
,而普通用户则不允许访问 a.html
; 而 元素级别权限 则是基于权限控制到用户是否可以看到、使用页面上某个元素,比如一个输入框、一个按钮。一旦用户可以绕过权限系统跨权限的访问不属于自己的信息,那么系统完全可能被摧毁,丢失数据、数据错误等等,造成不可估量的损失。
抛开权限设计的复杂度而言,权限反映到 UI 上的话只有 可用 和 不可用 的分别。在基于 RBAC
权限设计中,一般包含以下三种角色:
- User,即用户,比如张三、李四等
- Role,即角色,比如经理、副经理、普通职员等
- Permission,即权限,权限一般和实际可执行的操作或者菜单选项直接一一映射,比如操作打印机、审批请假单等,权限直接操作的对象就是资源(比如在开放平台中API接口是一种资源)
再复杂一点的,可能会引入用户组的概念,将用户和用户组关联,用户组和角色关联,进而关联权限信息等到一个用户组下面某个用户权限信息。
路由权限
在基于路由的权限中,在进入路由之前我们必须进行权限的校验,校验通过则可正常进入路由,否则拒绝进入并予以降级处理。
在前后端分离这种情况中,由于路由已经脱离了后端的管理(即前端路由),所以大部分时候权限问题都只能由前端来处理。好在 vue
也好、angular
也好、 React
也好,都提供了 路由钩子,允许我们在钩子中处理一些业务,(page.js
没有 hooks
的设计,但是它允许我们注册多个 callback
,callback
按序执行,和express
的 router
设计很像 )。
- 前端本地校验
在前端本地校验路由权限,则要求前端持有权限信息:用户的角色和角色的权限,在 hooks
里利用这些信息去做校验。这样的好处在于减少了 接口的交互,对于体验会有所提升 ;但也势必引入另外一个问题:数据安全问题。权限信息由前端持有,如何保证数据的安全性则是一个细细考虑的问题。
这种情况下,一种需要后端配置,权限和路由关系由后端保存持有,初始化成功后,前端动态去后端获取权限信息缓存下来,权限设计比较复杂的情况下,这种方案可能来说会比较好;而另外一种就是权限信息本身也由前端维护,这种方案更适合权限设计比较简单的情况下。
- 后端动态校验
另外一种方案则是在 hooks
里通过 接口 去后端校验权限。这种方案中前端不需要持有路由权限问题,每次校验都由 后端处理,前端将必要数据 post
给后端,后端去校验权限信息, 前端再根据接口的返回做处理。这样的话,权限的私密性得以尽可能的保密,但是增加了 接口交互的过程,体验上可能会打折扣。
当然,上面两种方案也可以结合起来使用,前端做一次 缓存,对于校验过的路由前端缓存一次,再次访问的时候先判断本地权限信息,有的话使用本地的信息,反之则通过后端校验。
两种方案设计和实现上没有什么本质差别,也没有谁比谁更好的区别。通过哪种方案实现,是一个综合考虑的问题,比如权限设计的复杂度、权限信息的敏感度等等。
还有一种特殊的设计即路由依旧是后端管理,前端的页面只做了模板渲染的业务。每次路由跳转都请求到了后端 , 再由后端决定渲染哪个模板。这种情况下则可以直接由后端去处理权限。
元素权限
元素的权限控制比路由权限相对来说复杂一些,粒度细化到了 页面的上的元素,也许用户可以看到这个页面,但是看不到页面上的某个按钮。
这种情况的权限,一般很容易想到的模板表达式,即通过模板的逻辑判断来控制元素的隐藏显示。
<div>
<!-- 通过 checkAuth 函数来校验权限 -->
<button v-if="checkAuth()">删除</button>
</div>
这种方案可能是最简单粗暴的解决方案了。但是它也有一些弊端:
- 前端必须知道权限信息
- 前端必须在需要控制的地方都加上判断
- 有心人可以通过模板源码反推出一些信息。
因为涉及到了前端持有权限的问题,也面临了和 路有权限一样的问题:数据安全和权限复杂度。
所以还有另外一种方案:预生成,在构建阶段或者在线上预生成模板。通过模板标记的形式,由工具动态的生成代码出来,这样页面上不存在逻辑判断,前端拿到的模板就是最终的模板,直接渲染数据即可。
<div>
<!-- @auth:editor admin -->
<button>编辑</button>
<!-- @auth:editor admin -->
<!-- @auth:root -->
<button>删除</button>
<!-- @auth:root -->
<button>阅读</button>
</div>
在模板上通过打标记的形式标记出权限差异区,再通过工具生成对应的不同权限的模板,最终返回给前端进行渲染。
这里不管是在前端构建期处理还是在服务器上访问时候生成理论上都是可以的,对最终结果不影响。
当然,预生成的方案只是个理论,在实践上还是有很多东西要处理的,比如在 vue
、 angular
中 构建之后,模板内容被转成 js 代码。当浏览器加载完 JS 代码后,其实模板已经被下载好了,如何根据权限去加载不同的模板就是一个要解决问题了。
即使在 angularjs
里,一旦模板加载过了之后,就会被缓存在本地了,如何刷新也是一个问题。
结束
乱七八糟的算是结束了。前后端分离的情况下,一些以前前端不曾处理的问题都会被抛出来要解决,而由于前端的特殊性(跑在客户端),很多东西就会受到制肘,不能直接拿后端的方案来套。当然很大程度上,前端的一些东西还是可以去借鉴后端的设计和理念。
对于权限的问题,不管是在后端还是前端,始终都是一个大而广的问题,不是一句两句话可以讲清道明的,也不是多少代码量就可以解决的问题?权限也好、数据也好¿
即使抛开权限问题来说,前端的安全性都是需要时时刻刻关注的问题,权限也好、数据也好,用户都是不可信任的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。