前后端分離模式下的權(quán)限設(shè)計方案?
https://mp.weixin.qq.com/s/OGyMUWXB3DuvNv-MGsbKiA
來源:www.cnblogs.com/liuyh/p/8027833.html
前后端分離模式下,所有的交互場景都變成了數(shù)據(jù),傳統(tǒng)業(yè)務(wù)系統(tǒng)中的權(quán)限控制方案在前端已經(jīng)不再適用,因此引發(fā)了我對權(quán)限的重新思考與設(shè)計。
權(quán)限控制到底控制的是什么?
在理解權(quán)限控制之前,需要明白兩個概念:資源和權(quán)限。什么是資源,對于一個系統(tǒng)來說,系統(tǒng)內(nèi)部的所有信息都可以理解為這個系統(tǒng)的資源。頁面是資源、數(shù)據(jù)是資源、按鈕是資源、圖片是資源、甚至頁面上一條分割線也可理解為是這個系統(tǒng)的資源。
而權(quán)限就是訪問某個資源所需要的標(biāo)識。無論系統(tǒng)的權(quán)限如何設(shè)計,在用戶登錄時,都可以計算得出用戶所擁有的權(quán)限標(biāo)識集合,也就確定了該用戶能訪問哪些系統(tǒng)資源,這就是我理解的權(quán)限控制的本質(zhì)。于是我們可以得出:權(quán)限控制是控制登錄用戶對于系統(tǒng)資源的訪問。
前后端分離模式下,前后端在權(quán)限控制中各自的職責(zé)是什么?
在弄清前后端在權(quán)限控制中各自的職責(zé)是什么之前,需要理解前后端各自在系統(tǒng)中的職責(zé)。這個還是很好理解:
服務(wù)端:提供數(shù)據(jù)接口。
前端:路由控制、頁面渲染。
由于前端負(fù)責(zé)與用戶交互,用戶所能操作的資源入口都是由前端進(jìn)行控制,那么前端的權(quán)限控制就包括:
前端路由的權(quán)限控制,過濾非法請求,用戶只能訪問權(quán)限范圍內(nèi)的頁面資源。
頁面內(nèi)組件的權(quán)限控制,根據(jù)用戶的權(quán)限控制頁面組件的渲染。包括各種按鈕、表格、分割線等。
隨著前端組件化的快速發(fā)展,用戶所看到的一切均可理解為組件,頁面是個大組件,其內(nèi)部由各個小組件拼湊而來,那么前端權(quán)限控制最終落地到對組件的權(quán)限控制。于是腦補了出了最優(yōu)雅的權(quán)限組件使用方式:
<組件permissionName='xxx'/>
前端可以渲染出用戶權(quán)限范圍內(nèi)的各種系統(tǒng)資源,但是不能保證數(shù)據(jù)接口的安全性,某些比較喜歡折騰的用戶完全可以越過前端的頁面訪問我們系統(tǒng)的數(shù)據(jù)接口,那么服務(wù)端的權(quán)限控制最終落地到對接口的權(quán)限驗證。
實現(xiàn)思路
引上文,系統(tǒng)的一切資源均可進(jìn)行權(quán)限控制,實際上也可以做到,但在我們實際的操作過程中,往往不需要細(xì)化到分割線那種程度。這里以按鈕級權(quán)限控制為例做實現(xiàn)說明,如果有更細(xì)粒度的權(quán)限需求,此思路依然可行。
前端路由權(quán)限控制。用戶登錄時拿到用戶擁有的權(quán)限標(biāo)識集合,在前端存儲。路由變化時,進(jìn)行權(quán)限判斷,通過則渲染對應(yīng)頁面組件,否則渲染403組件。示例代碼:
let hasPermission?=?permission.check(current.permissionName);
{hasPermission???children?:}
封裝bird-button權(quán)限按鈕組件,傳入按鈕所需權(quán)限名,內(nèi)部進(jìn)行權(quán)限判斷,通過則渲染按鈕。
測試按鈕
服務(wù)端。服務(wù)端權(quán)限驗證很好理解。使用攔截器驗證當(dāng)前請求的權(quán)限。代碼示例:
publicclassSsoAuthorizeInterceptorextendsHandlerInterceptorAdapter{
@Autowired
privateTicketHandler?ticketHandler;
@Autowired
privateSsoAuthorizeManager?authorizeManager;
@Override
publicbooleanpreHandle(HttpServletRequest?request,?HttpServletResponse?response,?Object?handler)throwsException{
if(!(handlerinstanceofHandlerMethod))returnfalse;
HandlerMethod?handlerMethod?=?(HandlerMethod)?handler;
SsoAuthorize?authorize?=?handlerMethod.getMethodAnnotation(SsoAuthorize.class);
if(authorize?!=null)?{
TicketInfo?ticketInfo?=?ticketHandler.getTicket(request);
if(ticketInfo?==null)?{
thrownewUnAuthorizedException("用戶信息已失效.");
}
String[]?requirePermissions?=?authorize.permissions();
if(requirePermissions.length==0)returntrue;
booleanisCheckAll?=?authorize.isCheckAll();
UserPermissionChecker?permissionChecker?=?authorizeManager.getUserPermissionChecker();
if(!permissionChecker.hasPermissions(ticketInfo.getUserId(),requirePermissions,isCheckAll)){
thrownewForbiddenException("用戶沒有當(dāng)前操作的權(quán)限.");
}
}
returntrue;
}
}
源碼地址
本博客涉及到的前端權(quán)限控制思路均已在:
https://github.com/liuxx001/bird-front
項目中實現(xiàn),項目中除了按鈕級權(quán)限方案還提供了后臺業(yè)務(wù)系統(tǒng)開發(fā)中常用的數(shù)據(jù)組件,包括:
下拉選擇器:bird-selector。
https://github.com/liuxx001/bird-front/blob/master/doc/bird-selector.md
全自動數(shù)據(jù)表格:bird-grid。
https://github.com/liuxx001/bird-front/blob/master/doc/bird-grid.md
全自動樹表: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
全自動表單: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)行組件的封裝,兼顧靈活性的同時保證更優(yōu)的業(yè)務(wù)開發(fā)速度。
歡迎指正,提出不同的看法。
(完)