Skip to content
geelevelgeelevel

๐Ÿ›ก๏ธ 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.act

Configuration Parameter Description โ€‹

Configuration ItemDescription
request_definitionRequest definition: subject (sub), object (obj), action (act)
policy_definitionPolicy definition: permission rule format
role_definitionRole definition: role inheritance relationships
policy_effectPolicy effect: conditions for allowing access
matchersMatchers: 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 โ€‹

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.