๐ก๏ธ Permission System โ
Gin-Vue-Admin uses Casbin to implement Role-Based Access Control (RBAC), providing a flexible and powerful permission management mechanism that supports multi-level permission control.
๐ฏ Permission Model Overview โ
RBAC Permission Model โ
User โโโ
โโโ Role โโโ Permission โโโ Resource
Group โPermission Hierarchy Structure โ
mermaid
graph TD
A[Super Admin] --> B[System Admin]
A --> C[Business Admin]
B --> D[Regular User]
C --> D
B --> E[User Management Permission]
B --> F[System Configuration Permission]
C --> G[Business Data Permission]
D --> H[Basic View Permission]
E --> I[API: /user/*]
F --> J[API: /system/*]
G --> K[API: /business/*]
H --> L[API: /base/*]๐ง Casbin Configuration โ
Model Configuration File โ
Location: server/resource/rbac_model.conf
ini
[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.actConfiguration Parameter Description โ
| Configuration Item | Description |
|---|---|
request_definition | Request definition: subject (sub), object (obj), action (act) |
policy_definition | Policy definition: permission rule format |
role_definition | Role definition: role inheritance relationships |
policy_effect | Policy effect: conditions for allowing access |
matchers | Matchers: permission verification logic |
๐ ๏ธ Core Components โ
Casbin Middleware โ
Location: server/middleware/casbin_rbac.go
go
// CasbinHandler Casbin permission verification middleware
func CasbinHandler() gin.HandlerFunc {
return func(c *gin.Context) {
claims, _ := c.Get("claims")
waitUse := claims.(*utils.CustomClaims)
// Get request information
obj := c.Request.URL.Path
act := c.Request.Method
sub := waitUse.AuthorityId
// Casbin permission verification
e := casbinService.Casbin()
success, _ := e.Enforce(sub, obj, act)
if !success {
response.FailWithDetailed(gin.H{}, "Insufficient permissions", c)
c.Abort()
return
}
c.Next()
}
}Casbin Service โ
Location: server/service/sys_casbin.go
go
type CasbinService struct{}
// UpdateCasbin Updates Casbin permissions
func (casbinService *CasbinService) UpdateCasbin(authorityId string, casbinInfos []request.CasbinInfo) error {
casbinService.ClearCasbin(0, authorityId)
rules := [][]string{}
for _, v := range casbinInfos {
rules = append(rules, []string{authorityId, v.Path, v.Method})
}
e := casbinService.Casbin()
success, _ := e.AddPolicies(rules)
if !success {
return errors.New("Duplicate API exists, addition failed, please contact the administrator")
}
return nil
}
// GetPolicyPathByAuthorityId Retrieves permission list
func (casbinService *CasbinService) GetPolicyPathByAuthorityId(authorityId string) (pathMaps []request.CasbinInfo) {
e := casbinService.Casbin()
list := e.GetFilteredPolicy(0, authorityId)
for _, v := range list {
pathMaps = append(pathMaps, request.CasbinInfo{
Path: v[1],
Method: v[2],
})
}
return pathMaps
}
// ClearCasbin Clears permissions
func (casbinService *CasbinService) ClearCasbin(v int, p ...string) bool {
e := casbinService.Casbin()
success, _ := e.RemoveFilteredPolicy(v, p...)
return success
}
// Casbin Retrieves Casbin instance
func (casbinService *CasbinService) Casbin() *casbin.Enforcer {
return global.GVA_CASBIN
}๐๏ธ Permission Data Structure โ
Role Table (sys_authorities) โ
go
type SysAuthority struct {
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time `sql:"index"`
AuthorityId string `json:"authorityId" gorm:"not null;unique;primary_key;comment:่ง่ฒID;size:90"`
AuthorityName string `json:"authorityName" gorm:"comment:่ง่ฒๅ"`
ParentId string `json:"parentId" gorm:"comment:็ถ่ง่ฒID"`
DataAuthorityId []string `json:"dataAuthorityId" gorm:"-"`
Children []SysAuthority `json:"children" gorm:"-"`
SysBaseMenus []SysBaseMenu `json:"menus" gorm:"many2many:sys_authority_menus;"`
Users []SysUser `json:"-" gorm:"many2many:sys_user_authority;"`
DefaultRouter string `json:"defaultRouter" gorm:"comment:้ป่ฎค่ๅ;default:dashboard"`
}Permission Rule Table (casbin_rule) โ
go
type CasbinRule struct {
ID uint `gorm:"primaryKey;autoIncrement"`
Ptype string `gorm:"size:512;uniqueIndex:unique_index"`
V0 string `gorm:"size:512;uniqueIndex:unique_index"`
V1 string `gorm:"size:512;uniqueIndex:unique_index"`
V2 string `gorm:"size:512;uniqueIndex:unique_index"`
V3 string `gorm:"size:512;uniqueIndex:unique_index"`
V4 string `gorm:"size:512;uniqueIndex:unique_index"`
V5 string `gorm:"size:512;uniqueIndex:unique_index"`
}API Permission Table (sys_apis) โ
go
type SysApi struct {
global.GVA_MODEL
Path string `json:"path" gorm:"comment:api่ทฏๅพ"`
Description string `json:"description" gorm:"comment:apiไธญๆๆ่ฟฐ"`
ApiGroup string `json:"apiGroup" gorm:"comment:api็ป"`
Method string `json:"method" gorm:"default:POST;comment:ๆนๆณ"`
}๐๏ธ Permission Management Features โ
1. Role Management โ
Create Role โ
go
// CreateAuthority ๅๅปบ่ง่ฒ
func (authorityService *AuthorityService) CreateAuthority(auth system.SysAuthority) (authority system.SysAuthority, err error) {
var authorityBox system.SysAuthority
if !errors.Is(global.GVA_DB.Where("authority_id = ?", auth.AuthorityId).First(&authorityBox).Error, gorm.ErrRecordNotFound) {
return auth, errors.New("ๅญๅจ็ธๅ่ง่ฒid")
}
err = global.GVA_DB.Create(&auth).Error
return auth, err
}Role Inheritance โ
go
// ่ฎพ็ฝฎ่ง่ฒ็ปงๆฟๅ
ณ็ณป
e := casbinService.Casbin()
e.AddRoleForUser("user1", "role1") // ็จๆท็ปงๆฟ่ง่ฒ
e.AddRoleForUser("role1", "role2") // ่ง่ฒ็ปงๆฟ่ง่ฒ2. API Permission Management โ
Assign API Permissions โ
go
// UpdateCasbinApi ๆดๆฐAPIๆ้
func (casbinService *CasbinService) UpdateCasbinApi(oldPath string, newPath string, oldMethod string, newMethod string) error {
err := global.GVA_DB.Model(&gormadapter.CasbinRule{}).Where("v1 = ? AND v2 = ?", oldPath, oldMethod).Updates(map[string]interface{}{
"v1": newPath,
"v2": newMethod,
}).Error
e := casbinService.Casbin()
err = e.LoadPolicy()
return err
}API Permission Verification โ
go
// ๆ้้ช่ฏ็คบไพ
func checkPermission(userId, path, method string) bool {
e := casbinService.Casbin()
// ่ทๅ็จๆท่ง่ฒ
roles := e.GetRolesForUser(userId)
// ๆฃๆฅๆ้
for _, role := range roles {
if ok, _ := e.Enforce(role, path, method); ok {
return true
}
}
return false
}3. Menu Permission Management โ
Menu Permission Table (sys_base_menus) โ
go
type SysBaseMenu struct {
global.GVA_MODEL
MenuLevel uint `json:"-"`
ParentId string `json:"parentId" gorm:"comment:็ถ่ๅID"`
Path string `json:"path" gorm:"comment:่ทฏ็ฑpath"`
Name string `json:"name" gorm:"comment:่ทฏ็ฑname"`
Hidden bool `json:"hidden" gorm:"comment:ๆฏๅฆๅจๅ่กจ้่"`
Component string `json:"component" gorm:"comment:ๅฏนๅบๅ็ซฏๆไปถ่ทฏๅพ"`
Sort int `json:"sort" gorm:"comment:ๆๅบๆ ่ฎฐ"`
Meta `json:"meta" gorm:"embedded;comment:้ๅ ๅฑๆง"`
SysAuthoritys []SysAuthority `json:"authoritys" gorm:"many2many:sys_authority_menus;"`
Children []SysBaseMenu `json:"children" gorm:"-"`
Parameters []SysBaseMenuParameter `json:"parameters"`
MenuBtn []SysBaseMenuBtn `json:"menuBtn"`
}Dynamic Menu Generation โ
go
// GetMenuTree ่ทๅๅจๆ่ๅๆ
func (menuService *MenuService) GetMenuTree(authorityId string) (menus []system.SysMenu, err error) {
menuTree, err := menuService.getMenuTreeMap(authorityId)
menus = menuTree["0"]
for i := 0; i < len(menus); i++ {
err = menuService.getChildrenList(&menus[i], menuTree)
}
return menus, err
}4. Button Permission Management โ
Button Permission Table (sys_base_menu_btns) โ
go
type SysBaseMenuBtn struct {
global.GVA_MODEL
Name string `json:"name" gorm:"comment:ๆ้ฎๅ
ณ้ฎkey"`
Desc string `json:"desc" gorm:"comment:ๆ้ฎๅคๆณจ"`
SysBaseMenuID uint `json:"sysBaseMenuID" gorm:"comment:่ๅID"`
}Frontend Button Permission Control โ
vue
<template>
<!-- ไฝฟ็จ v-auth ๆไปคๆงๅถๆ้ฎๆพ็คบ -->
<el-button v-auth="'user:create'" @click="createUser">
ๅๅปบ็จๆท
</el-button>
<el-button v-auth="'user:delete'" @click="deleteUser">
ๅ ้ค็จๆท
</el-button>
</template>
<script>
// ๆ้ๆไปคๅฎ็ฐ
app.directive('auth', {
mounted(el, binding) {
const { value } = binding
const userStore = useUserStore()
if (!userStore.hasPermission(value)) {
el.style.display = 'none'
}
}
})
</script>๐ Permission Synchronization Mechanism โ
Permission Cache Update โ
go
// ๆ้ๅๆดๆถๅๆญฅ็ผๅญ
func (casbinService *CasbinService) FreshCasbin() (err error) {
e := casbinService.Casbin()
err = e.LoadPolicy()
return err
}
// ๆธ
้ค็จๆทๆ้็ผๅญ
func (casbinService *CasbinService) ClearUserCache(userId string) {
// ๆธ
้คRedisไธญ็็จๆทๆ้็ผๅญ
global.GVA_REDIS.Del(context.Background(), "user:permissions:"+userId)
}Real-time Permission Verification โ
go
// ๅฎๆถๆ้ๆฃๆฅ
func (casbinService *CasbinService) CheckPermission(userId, resource, action string) bool {
// 1. ๆฃๆฅ็ผๅญ
cacheKey := fmt.Sprintf("permission:%s:%s:%s", userId, resource, action)
if result, err := global.GVA_REDIS.Get(context.Background(), cacheKey).Result(); err == nil {
return result == "true"
}
// 2. ๅฎๆถ้ช่ฏ
e := casbinService.Casbin()
hasPermission, _ := e.Enforce(userId, resource, action)
// 3. ็ผๅญ็ปๆ
global.GVA_REDIS.Set(context.Background(), cacheKey, hasPermission, time.Minute*5)
return hasPermission
}๐จ Frontend Permission Integration โ
Route Permission Control โ
javascript
// router/permission.js
import { useUserStore } from '@/pinia/modules/user'
router.beforeEach(async (to, from, next) => {
const userStore = useUserStore()
// ๆฃๆฅ็ปๅฝ็ถๆ
if (!userStore.token) {
if (to.path !== '/login') {
return next('/login')
}
return next()
}
// ๆฃๆฅ่ทฏ็ฑๆ้
if (to.meta.requiresAuth) {
const hasPermission = await userStore.checkRoutePermission(to.path)
if (!hasPermission) {
return next('/403')
}
}
next()
})API Permission Interception โ
javascript
// utils/request.js
import axios from 'axios'
import { useUserStore } from '@/pinia/modules/user'
// ่ฏทๆฑๆฆๆชๅจ
axios.interceptors.request.use(
config => {
const userStore = useUserStore()
// ๆทปๅ Token
if (userStore.token) {
config.headers['x-token'] = userStore.token
}
return config
},
error => Promise.reject(error)
)
// ๅๅบๆฆๆชๅจ
axios.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 403) {
// ๆ้ไธ่ถณๅค็
ElMessage.error('ๆ้ไธ่ถณ')
return Promise.reject(error)
}
if (error.response?.status === 401) {
// Token ่ฟๆๅค็
const userStore = useUserStore()
userStore.logout()
router.push('/login')
}
return Promise.reject(error)
}
)๐ Security Best Practices โ
1. Principle of Least Privilege โ
- Users only receive the minimum permissions necessary to perform their work.
- Regularly review and clean up unnecessary permissions.
- Implement time-limited permissions.
2. Permission Separation โ
- Separate management permissions from business permissions.
- Separate read permissions from write permissions.
- Sensitive operations require additional verification.
3. Audit Logs โ
go
// ๆ้ๆไฝๆฅๅฟ
type PermissionLog struct {
UserID string `json:"user_id"`
Action string `json:"action"`
Resource string `json:"resource"`
Result bool `json:"result"`
IP string `json:"ip"`
UserAgent string `json:"user_agent"`
Timestamp time.Time `json:"timestamp"`
}
// ่ฎฐๅฝๆ้ๆไฝ
func LogPermissionCheck(userID, action, resource string, result bool, c *gin.Context) {
log := PermissionLog{
UserID: userID,
Action: action,
Resource: resource,
Result: result,
IP: c.ClientIP(),
UserAgent: c.GetHeader("User-Agent"),
Timestamp: time.Now(),
}
// ่ฎฐๅฝๅฐๆฐๆฎๅบๆๆฅๅฟๆไปถ
global.GVA_LOG.Info("Permission Check", zap.Any("log", log))
}๐ Common Issues โ
Q: Permissions not taking effect after modification? โ
A: You need to call e.LoadPolicy() to reload the permission policy, or restart the application.
Q: How to implement data permission control? โ
A: You can add a data scope field in the Casbin rule, or use a custom data filter.
Q: How to optimize permission verification performance? โ
A: Use Redis to cache permission results, and set a reasonable cache expiration time.
Q: How to implement temporary permissions? โ
A: You can add a time field in the permission rule, or use a scheduled task to clean up expired permissions.


