一. 前言
【有來】開源全棧項目版本更新,本文部分內容和項目源碼有出入,建議移步至 【Spring Cloud & Alibaba 實戰 | 總結篇】Spring Cloud Gateway + Spring Security OAuth2 + JWT 實現微服務統一認證授權和鑒權,基於RBAC設計的適配微服務開發模式權限框架
hi,大家好,這應該是農歷年前的關於開源項目有來商城 的最后一篇文章了。
有來商城 是基於 Spring Cloud OAuth2 + Spring Cloud Gateway + JWT實現的統一認證鑒權,Spring Cloud & Alibaba + vue-element-admin實現的微服務、前后端分離的全棧開源項目。
有來商城 的權限設計主要是為了實現以下幾點目標:
-
實現RBAC模式的權限管理設計
-
實現基於vue-element-admin后台菜單權限管理系統
-
Spring Cloud Gateway網關針對RESTful接口權限控制
-
Vue自定義指令實現按鈕級別權限控制
二. 項目介紹
1. 項目簡介
有來商城 是基於Spring Boot 2.4、Spring Cloud 2020 & Alibaba、Vue、element-ui、uni-app快速構建的一套全棧開源商城平台,包括微服務應用、管理平台、微信小程序及APP應用。
2. 項目地址
項目預覽地址: http://www.youlai.store
微信小程序體驗碼:

源碼地址:
| 項目名稱 | Github | 碼雲 |
|---|---|---|
| 微服務后台 | youlai-mall | youlai-mall |
| 管理前端 | youlai-mall-admin | youlai-mall-admin |
| 微信小程序 | youlai-mall-weapp | youlai-mall-weapp |
| APP應用 | youlai-mall-app | youlai-mall-app |
3. 項目往期文章
后台微服務
- Spring Cloud實戰 | 第一篇:Windows搭建Nacos服務
- Spring Cloud實戰 | 第二篇:Spring Cloud整合Nacos實現注冊中心
- Spring Cloud實戰 | 第三篇:Spring Cloud整合Nacos實現配置中心
- Spring Cloud實戰 | 第四篇:Spring Cloud整合Gateway實現API網關
- Spring Cloud實戰 | 第五篇:Spring Cloud整合OpenFeign實現微服務之間的調用
- Spring Cloud實戰 | 第六篇:Spring Cloud Gateway+Spring Security OAuth2+JWT實現微服務統一認證授權
- Spring Cloud實戰 | 最七篇:Spring Cloud Gateway+Spring Security OAuth2集成統一認證授權平台下實現注銷使JWT失效方案
- Spring Cloud實戰 | 最八篇:Spring Cloud +Spring Security OAuth2+ Vue前后端分離模式下無感知刷新實現JWT續期
- Spring Cloud實戰 | 最九篇:Spring Security OAuth2認證服務器統一認證自定義異常處理
- Spring Cloud實戰 | 第十篇 :Spring Cloud + Nacos整合Seata 1.4.1最新版本實現微服務架構中的分布式事務,進階之路必須要邁過的檻
后台管理前端
- vue-element-admin實戰 | 第一篇: 移除mock接入微服務接口,搭建SpringCloud+Vue前后端分離管理平台
- vue-element-admin實戰 | 第二篇: 最小改動接入后台實現根據權限動態加載菜單
微信小程序
應用部署
- Docker實戰 | 第一篇:Linux 安裝 Docker
- Docker實戰 | 第二篇:Docker部署nacos-server:1.4.0
- Docker實戰 | 第三篇:IDEA集成Docker插件實現一鍵自動打包部署微服務項目,一勞永逸的技術手段值得一試
- Docker實戰 | 第四篇:Docker安裝Nginx,實現基於vue-element-admin框架構建的項目線上部署
- Docker實戰 | 第五篇:Docker啟用TLS加密解決暴露2375端口引發的安全漏洞,被黑掉三台雲主機的教訓總結
三. 數據庫設計
RBAC(Role-Based Access Control)基於角色訪問控制,目前使用最為廣泛的權限模型。
此模型有三個角色用戶、角色和權限,在傳統的權限模型用戶直接關聯加了角色層,解耦了用戶和權限,使得權限系統有了更清晰的職責划分和更高的靈活度。
以下是有來系統關於RBAC權限模型的數據庫

用戶和角色關系不用過多說明,這里重點說下權限,首先系統的權限分為3類,具體如下表:
| 權限名稱 | 表名 | 字段 | 權限標識 |
|---|---|---|---|
| 菜單權限 | sys_menu | ||
| 接口權限 | sys_permission | type=1 | PUT_/users/** |
| 按鈕權限 | sys_permission | type=2 | system:user:add |
其實了解過目前主流開源系統的權限設計,大概率的把菜單和按鈕放一塊然后根據類別字段區分,以下就關於這種方式優劣發表下個人意見,僅供大家參考下不必較真:
優勢:
- 理論上合理,按鈕肯定屬於某個菜單之下
- 省去了權限表(sys_permission)和關聯中間表這兩張表
劣勢:
- 菜單模塊變的復雜了,菜單表多了和菜單無關聯的類型字段和權限標識字段
- 菜單和按鈕查詢要區分類型,給代碼開發帶來復雜和影響查詢性能
- 不能同時滿足按鈕權限控制和網關根據請求路徑Ant匹配鑒權(具體下文說)
四. 權限管理系統
先看下vue-element-admin下的RBAC模型下的后台權限管理界面,體驗地址:http://www.youlai.store
- 菜單權限管理

- 角色分配權限

五. RESTful接口權限控制
1. 接口和按鈕的權限標識的區別
上文說到的關於權限表的拆分,菜單單獨一張表,按鈕權限和接口權限合為一張表根據類型type字段區分,之所以這樣因為接口和按鈕權限有些共性,都有一個權限標識字段。
至於按鈕和接口為什么要區分呢?都使用system:user:add權限標識不可以嗎?
具體做法是接口方法加上Spring Security的注解@PreAuthorize("hasPermission('system:user:add')"),在執行方法前判斷用戶時候擁有該權限。
答案是一般場景這樣設計絕對沒問題。但這里使用網關作為統一鑒權的入口,肯定希望網關一次性把鑒權的活做的干脆利落,這樣就不需要在各個微服務單獨的把Spring Security權限模塊引入鑒權,通過網關鑒權能把職責分工明確,減少開發工作量,無權限的請求直接被網關攔截返回,不會走到微服務那里再被告知無權訪問,提高請求效率。
Spring Cloud Gateway網關使用請求路徑Ant匹配請求標識進行權限判斷的,例如/users/1經過Ant匹配到權限標識/users/**,而/users/**是被用戶所持有的權限標識,這就標識用戶允許訪問/users/1的請求,所以和按鈕的權限標識system:user:add是有區別的。
這樣就完事了嗎?當然還沒,因為 有來系統 較於其他系統它是比較嚴格遵守REST接口設計規范,所以如果僅僅是上面根據請求路徑URL判斷權限肯定是不合理的,/users/1這個請求路徑在RESTful接口下可能是GET類型的請求也有可能是PUT類型的請求,那該如何處理?
所以在sys_permission表里還有一個method字段來標識請求方法類型,值可能會是、GET、POST、PUT、PATCH、DELETE等HTTP請求方法類型,其中是不限請求方法類型的意思,然后將請求方法類型和請求路徑組合得到接口的權限標識是這樣的PUT_users/1。
接下來就通過對 有來系統 的實戰操作來演示網關如何細粒度對RESTful接口的權限控制。
2. 添加權限
新增用戶管理的增刪改查權限

3. 角色授權
賦予系統管理員(admin)用戶查詢權限,無其他權限


4. 加載角色權限規則數據至緩存
項目啟動查看Redis中的角色權限規則:

看到系統管理員這個角色是沒有用戶修改權限的。你可以給角色添加用戶修改權限后嘗試是否可以修改成功。
5. 接口權限控制演示
admin系統管理員登錄執行一個用戶修改的提交的請求,看一下網關鑒權的流程:

結果可想而知,系統管理員不具有修改用戶PUT_/youlai-admin/v1/users/2權限,從緩存查詢只有超級管理員具有該接口請求方法訪問權限。頁面結果顯示如下:

六. 按鈕權限控制
1. 什么是Vue自定義指令?
Vue除了核心功能默認內置的指令 (v-model 和 v-show),Vue 也允許注冊自定義指令。
這里主要使用Vue.directive注冊一個全局自定義指令v-has- permission用於權限判斷,然后在模板中的任何元素使用v-has- permission屬性。
2. 添加按鈕權限

3. 角色授權

4. 加載角色按鈕權限數據
完整代碼:youlai-mall-admin
登錄成功時獲取用戶信息,其中包含該用戶擁有的權限字符串集合如下:

這里將用戶權限擁有的字符串集合緩存到vuex的perms屬性中:

5. 自定義和注冊全局指令
有來管理前端 是基於vue-element-admin后台前端解決方案,在vue-element-admin項目我們可以看到自定義指令的應用。如下:

然后復制一份permission.js重命名為hasPermission.js,修改后如下:
import store from '@/store'
// 校驗用戶是否擁有按鈕權限
function hasPermission(el, binding) {
const {value} = binding
const perms = store.getters && store.getters.perms
if (value && value instanceof Array) {
if (value.length > 0) {
const requiredPerms = value
const hasPermission = perms.some(perm => {
return requiredPerms.includes(perm)
})
if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el)
}
}
} else {
throw new Error(`need perms! Like v-has-permission="['system:user:add','system:user:edit']"`)
}
}
export default {
inserted(el, binding) {
hasPermission(el,binding)
},
update(el, binding) {
hasPermission(el,binding)
}
}
注冊hasPermission至全局指令:


指令在組件上的應用:

6. 按鈕權限控制演示
系統管理員是沒有修改按鈕的權限的,結果如下頁面不顯示修改按鈕。

那給系統管理員添加修改按鈕的權限,再看看用戶頁面的顯示情況


此時用戶頁面的修改按鈕已經顯示出來了,至此完成了系統的按鈕權限控制。
七. 結語
本篇通過實戰的方式講述如何基於Spring Cloud Gateway + vue-element-admin技術設計一套符合RBAC規范的權限管理系統,通過網關就可以輕易實現RESTful接口方法細粒度的控制,無需將Spring Security模塊引入各個微服務;以及使用Vue的自定義指令在組件中使用實現細粒度的按鈕權限控制。
如果你對此系統權限設計有更好的建議,歡迎留言給我,在此感謝!如果對項目感興趣的話,歡迎加我微信和項目交流群。
最后預祝大家新年愉快,有個完美充實的小假期。
