前后端分离模式下的权限设计方案
前后端分離模式下,所有的交互場(chǎng)景都變成了數(shù)據(jù),傳統(tǒng)業(yè)務(wù)系統(tǒng)中的權(quán)限控制方案在前端已經(jīng)不再適用,因此引發(fā)了我對(duì)權(quán)限的重新思考與設(shè)計(jì)。
權(quán)限控制到底控制的是什么?
在理解權(quán)限控制之前,需要明白兩個(gè)概念:資源和權(quán)限。什么是資源,對(duì)于一個(gè)系統(tǒng)來(lái)說,系統(tǒng)內(nèi)部的所有信息都可以理解為這個(gè)系統(tǒng)的資源。頁(yè)面是資源、數(shù)據(jù)是資源、按鈕是資源、圖片是資源、甚至頁(yè)面上一條分割線也可理解為是這個(gè)系統(tǒng)的資源。
而權(quán)限就是訪問某個(gè)資源所需要的標(biāo)識(shí)。無(wú)論系統(tǒng)的權(quán)限如何設(shè)計(jì),在用戶登錄時(shí),都可以計(jì)算得出用戶所擁有的權(quán)限標(biāo)識(shí)集合,也就確定了該用戶能訪問哪些系統(tǒng)資源,這就是我理解的權(quán)限控制的本質(zhì)。于是我們可以得出:權(quán)限控制是控制登錄用戶對(duì)于系統(tǒng)資源的訪問。
前后端分離模式下,前后端在權(quán)限控制中各自的職責(zé)是什么?
在弄清前后端在權(quán)限控制中各自的職責(zé)是什么之前,需要理解前后端各自在系統(tǒng)中的職責(zé)。這個(gè)還是很好理解:
-
服務(wù)端:提供數(shù)據(jù)接口。
-
前端:路由控制、頁(yè)面渲染。
由于前端負(fù)責(zé)與用戶交互,用戶所能操作的資源入口都是由前端進(jìn)行控制,那么前端的權(quán)限控制就包括:
前端路由的權(quán)限控制,過濾非法請(qǐng)求,用戶只能訪問權(quán)限范圍內(nèi)的頁(yè)面資源。
頁(yè)面內(nèi)組件的權(quán)限控制,根據(jù)用戶的權(quán)限控制頁(yè)面組件的渲染。包括各種按鈕、表格、分割線等。
隨著前端組件化的快速發(fā)展,用戶所看到的一切均可理解為組件,頁(yè)面是個(gè)大組件,其內(nèi)部由各個(gè)小組件拼湊而來(lái),那么前端權(quán)限控制最終落地到對(duì)組件的權(quán)限控制。于是腦補(bǔ)了出了最優(yōu)雅的權(quán)限組件使用方式:
<組件?permissionName='xxx'?/>前端可以渲染出用戶權(quán)限范圍內(nèi)的各種系統(tǒng)資源,但是不能保證數(shù)據(jù)接口的安全性,某些比較喜歡折騰的用戶完全可以越過前端的頁(yè)面訪問我們系統(tǒng)的數(shù)據(jù)接口,那么服務(wù)端的權(quán)限控制最終落地到對(duì)接口的權(quán)限驗(yàn)證。
實(shí)現(xiàn)思路
引上文,系統(tǒng)的一切資源均可進(jìn)行權(quán)限控制,實(shí)際上也可以做到,但在我們實(shí)際的操作過程中,往往不需要細(xì)化到分割線那種程度。這里以按鈕級(jí)權(quán)限控制為例做實(shí)現(xiàn)說明,如果有更細(xì)粒度的權(quán)限需求,此思路依然可行。
前端路由權(quán)限控制。用戶登錄時(shí)拿到用戶擁有的權(quán)限標(biāo)識(shí)集合,在前端存儲(chǔ)。路由變化時(shí),進(jìn)行權(quán)限判斷,通過則渲染對(duì)應(yīng)頁(yè)面組件,否則渲染403組件。示例代碼:
let?hasPermission?=?permission.check(current.permissionName);<div?className={styles.content}>{hasPermission???children?:?<Exception?type={403}/>} </div>封裝bird-button權(quán)限按鈕組件,傳入按鈕所需權(quán)限名,內(nèi)部進(jìn)行權(quán)限判斷,通過則渲染按鈕。
<BirdButton?permissionName={'sys'}?type='primary'>測(cè)試按鈕</BirdButton>服務(wù)端。服務(wù)端權(quán)限驗(yàn)證很好理解。使用攔截器驗(yàn)證當(dāng)前請(qǐng)求的權(quán)限。代碼示例:
public?class?SsoAuthorizeInterceptor?extends?HandlerInterceptorAdapter?{@Autowiredprivate?TicketHandler?ticketHandler;@Autowiredprivate?SsoAuthorizeManager?authorizeManager;@Overridepublic?boolean?preHandle(HttpServletRequest?request,?HttpServletResponse?response,?Object?handler)?throws?Exception?{if?(!(handler?instanceof?HandlerMethod))?return?false;HandlerMethod?handlerMethod?=?(HandlerMethod)?handler;SsoAuthorize?authorize?=?handlerMethod.getMethodAnnotation(SsoAuthorize.class);if?(authorize?!=?null)?{TicketInfo?ticketInfo?=?ticketHandler.getTicket(request);if?(ticketInfo?==?null)?{throw?new?UnAuthorizedException("用戶信息已失效.");}String[]?requirePermissions?=?authorize.permissions();if(requirePermissions.length==0)return?true;boolean?isCheckAll?=?authorize.isCheckAll();UserPermissionChecker?permissionChecker?=?authorizeManager.getUserPermissionChecker();if(!permissionChecker.hasPermissions(ticketInfo.getUserId(),requirePermissions,isCheckAll)){throw?new?ForbiddenException("用戶沒有當(dāng)前操作的權(quán)限.");}}return?true;} }源碼地址
本博客涉及到的前端權(quán)限控制思路均已在:
https://github.com/liuxx001/bird-front
項(xiàng)目中實(shí)現(xiàn),項(xiàng)目中除了按鈕級(jí)權(quán)限方案還提供了后臺(tái)業(yè)務(wù)系統(tǒng)開發(fā)中常用的數(shù)據(jù)組件,包括:
下拉選擇器:bird-selector。
https://github.com/liuxx001/bird-front/blob/master/doc/bird-selector.md
全自動(dòng)數(shù)據(jù)表格:bird-grid。
https://github.com/liuxx001/bird-front/blob/master/doc/bird-grid.md
全自動(dòng)樹表:bird-tree-grid。
https://github.com/liuxx001/bird-front/blob/master/doc/bird-tree-grid.md
數(shù)據(jù)樹:bird-tree。
https://github.com/liuxx001/bird-front/blob/master/doc/bird-tree.md
全自動(dòng)表單:bird-form。
https://github.com/liuxx001/bird-front/blob/master/doc/bird-form.md
權(quán)限按鈕:bird-button。
https://github.com/liuxx001/bird-front/blob/master/doc/bird-button.md
所有業(yè)務(wù)組件的理念均是結(jié)合服務(wù)端接口進(jìn)行組件的封裝,兼顧靈活性的同時(shí)保證更優(yōu)的業(yè)務(wù)開發(fā)速度。
歡迎指正,提出不同的看法。
總結(jié)
以上是生活随笔為你收集整理的前后端分离模式下的权限设计方案的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 死磕单点登录的实现原理
- 下一篇: 面试问Kafka,这一篇全搞定