最近一直在學(xué)習(xí) Vue3 相關(guān)的技術(shù)棧,包括 CompositionAPI 、TypeScript、vite以及Element UI 對 Vue3 的支持版本 Element Plus。
因此想要使用 Vue3 寫一個(gè)簡單的 RBAC 的用戶權(quán)限系統(tǒng)。
之前一直是使用 MySQL 這個(gè)關(guān)系型數(shù)據(jù)庫,正好最近在學(xué)習(xí) Kong 網(wǎng)關(guān)的時(shí)候,接觸到了 Postgres 這個(gè)關(guān)系型數(shù)據(jù)庫,并且還看到了一句話,說是:“MySQL 是目前使用最廣泛的數(shù)據(jù)庫,但是 Postgres 是目前最先進(jìn)的數(shù)據(jù)庫?!?/p>
當(dāng)然這個(gè)最先進(jìn)也是 Postgres 的開源組織自己標(biāo)榜的。但是對我來說,Postgres 貌似確實(shí)要比 MySQL 用起來爽,最起碼在數(shù)據(jù)類型的支持方面,就是一個(gè)非常不錯(cuò)的點(diǎn)。
我經(jīng)常會使用 Json 或者 Array 這種字段,Postgres 就支持的非常不錯(cuò),當(dāng)然 MySQL 5.7 以后也是支持 Json 字段的,但是從性能和使用上來說,我還感覺還是 Postgres 用的更好一些,也不排除,是我自己對 MySQL 了解不深入。
好了,廢話不多說,我們來實(shí)際看看項(xiàng)目吧。
本項(xiàng)目不管是前后端都不會進(jìn)行過度的封裝,該封裝的封裝,不該封裝的就不會進(jìn)行封裝,不會為了裝 X 而過度封裝代碼,減少大家在看代碼的時(shí)間成本。
演示站點(diǎn):http://fdevops.com:8088
github:https://github.com/lanyulei/sky , 如果覺得還可以,還請不要吝嗇指尖的跳動,輕輕點(diǎn)上一個(gè) star 。
Casbin 權(quán)限控制
本系統(tǒng),使用 Casbin 作為接口的權(quán)限管理依賴,使用 RBAC 的方式進(jìn)行管理,支持用戶的多角色綁定。
Casbin 模型文件內(nèi)容如下:
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
其實(shí)就是 Casbin 官網(wǎng)上給出的 RBAC 模型內(nèi)容,如果需要使用自定義函數(shù),可參考官網(wǎng)自行添加即可。
https://casbin.org/docs/zh-CN/function
Casbin Gin 中間件的簡單封裝。
package permission
import (
"sky/pkg/conn"
"sky/pkg/logger"
"sky/pkg/tools/response"
"time"
"github.com/casbin/casbin/v2"
gormAdapter "github.com/casbin/gorm-adapter/v3"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
)
var Enforcer *casbin.SyncedEnforcer
func CasbinSetup() *casbin.SyncedEnforcer {
var (
err error
adapter *gormAdapter.Adapter
)
adapter, err = gormAdapter.NewAdapterByDBWithCustomTable(conn.Orm, nil, viper.GetString("casbin.tableName"))
if err != nil {
logger.Fatal("創(chuàng)建 casbin gorm adapter 失敗,錯(cuò)誤:%v", err)
}
Enforcer, err = casbin.NewSyncedEnforcer(viper.GetString("casbin.rbacModel"), adapter)
if err != nil {
logger.Fatal("創(chuàng)建 casbin enforcer 失敗,錯(cuò)誤:%v", err)
}
err = Enforcer.LoadPolicy()
if err != nil {
logger.Fatal("從數(shù)據(jù)庫加載策略失敗,錯(cuò)誤:%v", err)
}
// 定時(shí)同步策略
if viper.GetBool("casbin.isTiming") {
// 間隔多長時(shí)間同步一次權(quán)限策略,單位:秒
Enforcer.StartAutoLoadPolicy(time.Second * time.Duration(viper.GetInt("casbin.intervalTime")))
}
return Enforcer
}
func CheckPermMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
//獲取資源
obj := c.Request.URL.Path
//獲取方法
act := c.Request.Method
//獲取實(shí)體
sub := c.GetString("username")
isAdmin := c.GetBool("isAdmin")
if isAdmin {
c.Next()
} else {
//判斷策略中是否存在
if ok, _ := Enforcer.Enforce(sub, obj, act); ok {
c.Next()
} else {
response.Error(c, nil, response.NoPermissionError)
c.Abort()
}
}
}
}
菜單權(quán)限
菜單通過對角色可訪問的菜單標(biāo)識進(jìn)行權(quán)限控制,可使多個(gè)菜單標(biāo)識。
路由實(shí)例如下:
{
path: '/user',
name: 'User',
component: () => import('/@/views/user/list.vue'),
meta: {
title: '用戶列表',
auth: ['system:user:list'] // 此為路由標(biāo)識,只有角色關(guān)聯(lián)了此標(biāo)識,才可訪問。
},
}
校驗(yàn)當(dāng)前用戶是否有權(quán)限,用戶的權(quán)限標(biāo)識列表將存入到 Vuex 中,當(dāng)前系統(tǒng)給用戶定義了超級管理員的概念,因此當(dāng)遇到程序是超級管理員的話,則直接放行。
export function setFilterRoute(chil: any) {
let filterRoute: any = [];
chil.forEach((route: any) => {
if (route.meta.auth) {
route.meta.auth.forEach((metaAuth: any) => {
store.state.userInfos.userInfos.authPageList.forEach((auth: any) => {
// 如果是超級管理員,則直接通過
if (store.state.userInfos.userInfos.is_admin || metaAuth === auth) filterRoute.push({ ...route });
});
});
}
});
return filterRoute;
}
項(xiàng)目演示
菜單管理,進(jìn)行菜單創(chuàng)建、頁面元素創(chuàng)建及菜單綁定 API 接口。
為菜單綁定 API 接口方便進(jìn)行接口權(quán)限管理。
頁面元素管理,包括但是不限于按鈕。
接口管理,后端所有需要通過 Casbin 進(jìn)行接口校驗(yàn)的接口。
為角色授權(quán)。
截圖內(nèi)容,僅是功能的一部分,詳細(xì)的內(nèi)容,可自行訪問演示站點(diǎn)查看。
有任何問題,可以到此處進(jìn)行留言討論,https://www.fdevops.com 。