Gin RBAC权限模型实现
RBAC 是最常用的权限控制模型,通过用户-角色-权限三层结构实现灵活的访问控制。
RBAC 模型概述
RBAC(Role-Based Access Control)核心要素:
- 用户(User):系统使用者
- 角色(Role):权限集合的抽象
- 权限(Permission):对资源的操作许可
Go
用户 ──关联──> 角色 ──关联──> 权限 ──对应──> 资源操作
数据库设计
表结构
Go
// 用户表
type User struct {
ID uint `gorm:"primaryKey"`
Username string `gorm:"uniqueIndex;size:50"`
Password string `gorm:"size:255"`
Roles []Role `gorm:"many2many:user_roles"`
}
// 角色表
type Role struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"uniqueIndex;size:50"`
Description string `gorm:"size:200"`
Permissions []Permission `gorm:"many2many:role_permissions"`
}
// 权限表
type Permission struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"uniqueIndex;size:100"` // 如: user:create, user:delete
Description string `gorm:"size:200"`
Resource string `gorm:"size:50"` // 资源名称
Action string `gorm:"size:20"` // 操作类型
}
// 关联表由 GORM 自动创建
// user_roles: user_id, role_id
// role_permissions: role_id, permission_id
数据库迁移
Go
func InitDB() *gorm.DB {
db, err := gorm.Open(mysql.Open("dsn"), &gorm.Config{})
if err != nil {
panic("数据库连接失败")
}
// 自动迁移
db.AutoMigrate(&User{}, &Role{}, &Permission{})
// 初始化基础角色和权限
initPermissions(db)
initRoles(db)
return db
}
func initPermissions(db *gorm.DB) {
permissions := []Permission{
{Name: "user:read", Resource: "user", Action: "read", Description: "查看用户"},
{Name: "user:create", Resource: "user", Action: "create", Description: "创建用户"},
{Name: "user:update", Resource: "user", Action: "update", Description: "更新用户"},
{Name: "user:delete", Resource: "user", Action: "delete", Description: "删除用户"},
{Name: "article:read", Resource: "article", Action: "read", Description: "查看文章"},
{Name: "article:create", Resource: "article", Action: "create", Description: "创建文章"},
{Name: "article:update", Resource: "article", Action: "update", Description: "更新文章"},
{Name: "article:delete", Resource: "article", Action: "delete", Description: "删除文章"},
}
db.Create(&permissions)
}
func initRoles(db *gorm.DB) {
// 管理员角色
adminRole := Role{Name: "admin", Description: "管理员"}
db.Create(&adminRole)
db.Model(&adminRole).Association("Permissions").Append([]Permission{})
// 编辑角色
editorRole := Role{Name: "editor", Description: "编辑"}
db.Create(&editorRole)
// 只分配文章相关权限
// 普通用户角色
userRole := Role{Name: "user", Description: "普通用户"}
db.Create(&userRole)
// 只分配读取权限
}
权限查询实现
Go
// 获取用户所有权限
func GetUserPermissions(db *gorm.DB, userID uint) ([]string, error) {
var permissions []string
err := db.Table("permissions").
Select("permissions.name").
Joins("JOIN role_permissions ON permissions.id = role_permissions.permission_id").
Joins("JOIN user_roles ON role_permissions.role_id = user_roles.role_id").
Where("user_roles.user_id = ?", userID).
Pluck("permissions.name", &permissions)
return permissions, err
}
// 检查用户是否有特定权限
func HasPermission(db *gorm.DB, userID uint, permission string) bool {
var count int64
db.Table("permissions").
Joins("JOIN role_permissions ON permissions.id = role_permissions.permission_id").
Joins("JOIN user_roles ON role_permissions.role_id = user_roles.role_id").
Where("user_roles.user_id = ? AND permissions.name = ?", userID, permission).
Count(&count)
return count > 0
}
// 检查用户角色
func HasRole(db *gorm.DB, userID uint, roleName string) bool {
var count int64
db.Table("user_roles").
Joins("JOIN roles ON user_roles.role_id = roles.id").
Where("user_roles.user_id = ? AND roles.name = ?", userID, roleName).
Count(&count)
return count > 0
}
Gin 中间件实现
权限检查中间件
Go
func RBACMiddleware(db *gorm.DB, permission string) gin.HandlerFunc {
return func(c *gin.Context) {
// 从上下文获取用户 ID(需配合 JWT 中间件)
userID, exists := c.Get("user_id")
if !exists {
c.JSON(401, gin.H{"error": "未认证"})
c.Abort()
return
}
// 检查权限
if !HasPermission(db, userID.(uint), permission) {
c.JSON(403, gin.H{"error": "无权限访问"})
c.Abort()
return
}
c.Next()
}
}
// 角色检查中间件
func RoleMiddleware(db *gorm.DB, roles ...string) gin.HandlerFunc {
return func(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
c.JSON(401, gin.H{"error": "未认证"})
c.Abort()
return
}
hasRole := false
for _, role := range roles {
if HasRole(db, userID.(uint), role) {
hasRole = true
break
}
}
if !hasRole {
c.JSON(403, gin.H{"error": "角色权限不足"})
c.Abort()
return
}
c.Next()
}
}
路由配置
Go
func SetupRoutes(r *gin.Engine, db *gorm.DB) {
// 公开路由
r.POST("/login", LoginHandler)
r.POST("/register", RegisterHandler)
// 需认证路由
api := r.Group("/api")
api.Use(JWTAuthMiddleware())
{
// 用户管理 - 需管理员权限
users := api.Group("/users")
users.Use(RoleMiddleware(db, "admin"))
{
users.GET("", ListUsers) // user:read
users.POST("", CreateUser) // user:create
users.PUT("/:id", UpdateUser) // user:update
users.DELETE("/:id", DeleteUser) // user:delete
}
// 文章管理 - 不同权限
articles := api.Group("/articles")
{
articles.GET("", RBACMiddleware(db, "article:read"), ListArticles)
articles.POST("", RBACMiddleware(db, "article:create"), CreateArticle)
articles.PUT("/:id", RBACMiddleware(db, "article:update"), UpdateArticle)
articles.DELETE("/:id", RBACMiddleware(db, "article:delete"), DeleteArticle)
}
// 个人中心 - 无需额外权限
api.GET("/profile", ProfileHandler)
}
}
权限缓存优化
Go
import "sync"
type PermissionCache struct {
cache map[uint][]string // userID -> permissions
mu sync.RWMutex
ttl time.Duration
}
func NewPermissionCache(ttl time.Duration) *PermissionCache {
return &PermissionCache{
cache: make(map[uint][]string),
ttl: ttl,
}
}
func (pc *PermissionCache) Get(userID uint) ([]string, bool) {
pc.mu.RLock()
defer pc.mu.RUnlock()
permissions, exists := pc.cache[userID]
return permissions, exists
}
func (pc *PermissionCache) Set(userID uint, permissions []string) {
pc.mu.Lock()
defer pc.mu.Unlock()
pc.cache[userID] = permissions
}
func (pc *PermissionCache) Delete(userID uint) {
pc.mu.Lock()
defer pc.mu.Unlock()
delete(pc.cache, userID)
}
// 带缓存的权限中间件
func CachedRBACMiddleware(db *gorm.DB, cache *PermissionCache, permission string) gin.HandlerFunc {
return func(c *gin.Context) {
userID := c.GetUint("user_id")
// 从缓存获取
permissions, exists := cache.Get(userID)
if !exists {
// 从数据库加载
permissions, _ = GetUserPermissions(db, userID)
cache.Set(userID, permissions)
}
// 检查权限
for _, p := range permissions {
if p == permission {
c.Next()
return
}
}
c.JSON(403, gin.H{"error": "无权限"})
c.Abort()
}
}
角色分配接口
text
// 给用户分配角色
func AssignRole(db *gorm.DB, userID uint, roleID uint) error {
user := User{ID: userID}
role := Role{ID: roleID}
return db.Model(&user).Association("Roles").Append(&role)
}
// 移除用户角色
func RemoveRole(db *gorm.DB, userID uint, roleID uint) error {
user := User{ID: userID}
role := Role{ID: roleID}
return db.Model(&user).Association("Roles").Delete(&role)
}
// 给角色分配权限
func AssignPermission(db *gorm.DB, roleID uint, permissionIDs []uint) error {
role := Role{ID: roleID}
var permissions []Permission
db.Find(&permissions, permissionIDs)
return db.Model(&role).Association("Permissions").Append(&permissions)
}
// API 接口
func AssignRoleHandler(c *gin.Context) {
var req struct {
UserID uint `json:"user_id" binding:"required"`
RoleID uint `json:"role_id" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": "参数错误"})
return
}
if err := AssignRole(db, req.UserID, req.RoleID); err != nil {
c.JSON(500, gin.H{"error": "分配角色失败"})
return
}
// 清除缓存
permissionCache.Delete(req.UserID)
c.JSON(200, gin.H{"message": "角色分配成功"})
}
权限设计原则
| 原则 | 说明 |
|---|---|
| 最小权限 | 只授予必需权限 |
| 权责分离 | 关键操作需多角色确认 |
| 权限继承 | 支持角色层级继承 |
| 动态调整 | 支持运行时修改权限 |
注意:权限变更后需清除缓存,保证实时生效。
要点总结
- 三层模型:用户-角色-权限,通过关联表建立关系
- 数据库设计:使用 GORM many2many 自动管理关联表
- 中间件实现:基于权限名或角色名检查访问权限
- 性能优化:使用缓存减少数据库查询
- 权限命名:采用
resource:action格式,便于管理 - 安全原则:最小权限、权责分离、动态调整
📝 发现内容有误?点击此处直接编辑