From 375b305ab401b145bed64ed4032cc31ef56f945d Mon Sep 17 00:00:00 2001 From: "zhangkun9038@dingtalk.com" Date: Thu, 20 Feb 2025 11:29:57 +0800 Subject: [PATCH] aliyun-deepseek-r1 --- README.md | 221 ---- api/routes.go | 70 +- config/config.go | 87 +- controllers/auth.go | 128 ++- controllers/rbac.go | 247 +---- controllers/user.go | 51 +- go.mod | 18 +- go.sum | 26 +- init.sh | 32 + main.go | 30 +- middleware/auth.go | 24 +- middleware/rbac.go | 30 +- models/permission.go | 17 +- models/role.go | 10 +- models/user.go | 33 +- models/user_group.go | 19 +- models/user_role.go | 6 + repositories/db.go | 60 +- repositories/permission.go | 66 +- repositories/role.go | 84 +- repositories/user.go | 150 ++- repositories/user_group.go | 72 +- server/server.go | 29 +- services/auth.go | 46 +- services/rbac.go | 237 ----- services/user.go | 46 - sug.md | 2062 ------------------------------------ utils/jwt.go | 41 +- utils/redis.go | 83 -- utils/response.go | 37 +- 30 files changed, 705 insertions(+), 3357 deletions(-) delete mode 100644 README.md create mode 100755 init.sh create mode 100644 models/user_role.go diff --git a/README.md b/README.md deleted file mode 100644 index eef4eb9..0000000 --- a/README.md +++ /dev/null @@ -1,221 +0,0 @@ -### 认证相关路由 - -1. **注册用户** - ```sh - curl -X POST http://localhost:8080/auth/register \ - -H "Content-Type: application/json" \ - -d '{"username": "testuser", "password": "testpass", "email": "test@example.com"}' - ``` - -2. **登录用户** - ```sh - curl -X POST http://localhost:8080/auth/login \ - -H "Content-Type: application/json" \ - -d '{"username": "testuser", "password": "testpass"}' - ``` - -### 需要认证的路由 - -假设登录后返回的token为`your_jwt_token_here`。 - -#### 用户管理 - -3. **获取所有用户** - ```sh - curl -X GET http://localhost:8080/api/users \ - -H "Authorization: Bearer your_jwt_token_here" - ``` - -4. **获取单个用户** - ```sh - curl -X GET http://localhost:8080/api/users/1 \ - -H "Authorization: Bearer your_jwt_token_here" - ``` - -5. **更新用户** - ```sh - curl -X PUT http://localhost:8080/api/users/1 \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer your_jwt_token_here" \ - -d '{"name": "updated_name", "email": "updated_email@example.com"}' - ``` - -6. **删除用户** - ```sh - curl -X DELETE http://localhost:8080/api/users/1 \ - -H "Authorization: Bearer your_jwt_token_here" - ``` - -#### 角色管理 - -7. **创建新角色** - ```sh - curl -X POST http://localhost:8080/api/roles \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer your_jwt_token_here" \ - -d '{"name": "admin", "description": "Administrator role"}' - ``` - -8. **获取所有角色** - ```sh - curl -X GET http://localhost:8080/api/roles \ - -H "Authorization: Bearer your_jwt_token_here" - ``` - -9. **获取单个角色** - ```sh - curl -X GET http://localhost:8080/api/roles/1 \ - -H "Authorization: Bearer your_jwt_token_here" - ``` - -10. **更新角色** - ```sh - curl -X PUT http://localhost:8080/api/roles/1 \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer your_jwt_token_here" \ - -d '{"name": "updated_role_name", "description": "Updated description"}' - ``` - -11. **删除角色** - ```sh - curl -X DELETE http://localhost:8080/api/roles/1 \ - -H "Authorization: Bearer your_jwt_token_here" - ``` - -#### 权限管理 - -12. **创建新权限** - ```sh - curl -X POST http://localhost:8080/api/permissions \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer your_jwt_token_here" \ - -d '{"name": "create_user", "description": "Create user permission", "resource": "user", "action": "create"}' - ``` - -13. **获取所有权限** - ```sh - curl -X GET http://localhost:8080/api/permissions \ - -H "Authorization: Bearer your_jwt_token_here" - ``` - -14. **获取单个权限** - ```sh - curl -X GET http://localhost:8080/api/permissions/1 \ - -H "Authorization: Bearer your_jwt_token_here" - ``` - -15. **更新权限** - ```sh - curl -X PUT http://localhost:8080/api/permissions/1 \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer your_jwt_token_here" \ - -d '{"name": "updated_permission_name", "description": "Updated description", "resource": "user", "action": "update"}' - ``` - -16. **删除权限** - ```sh - curl -X DELETE http://localhost:8080/api/permissions/1 \ - -H "Authorization: Bearer your_jwt_token_here" - - -> 请用curl语句实现如下操作: -> -> 创建一个账号: -> fullname: 李晓, -> 用户名: lixiao -> 密码 abcd@1234, -> 邮箱 lixiao@zjmud.xyz -> 创建2个权限: -> dbGroup1Reader: -> resource: db1,db2 -> action: read -> dbGroup1Writer: -> resource: db1,db2 -> action: write -> 创建1个角色: -> db1RW, 包含Permissions: "dbGroup1Reader, dbGroup1Writer" -> 把 账号lixiao绑定角色 db1RW - -为了实现这些操作,我们需要使用 `curl` 命令来发送 HTTP 请求到你的 API。假设你的 API 端点是 `http://localhost:8080/api`,以下是具体的 `curl` 命令: - -### 1. 创建账号 -```sh -curl -X POST http://localhost:8080/auth/register \ --H "Content-Type: application/json" \ --d '{ - "fullname": "李晓", - "username": "lixiao", - "password": "abcd@1234", - "email": "lixiao@zjmud.xyz" -}' -``` - -### 2. 创建权限 `dbGroup1Reader` -```sh -curl -X POST http://localhost:8080/api/permissions \ --H "Content-Type: application/json" \ --d '{ - "name": "dbGroup1Reader", - "description": "Read access to db1 and db2", - "resource": "db1,db2", - "action": "read" -}' -``` - -### 3. 创建权限 `dbGroup1Writer` -```sh -curl -X POST http://localhost:8080/api/permissions \ --H "Content-Type: application/json" \ --d '{ - "name": "dbGroup1Writer", - "description": "Write access to db1 and db2", - "resource": "db1,db2", - "action": "write" -}' -``` - -### 4. 创建角色 `db1RW` 并包含权限 `dbGroup1Reader` 和 `dbGroup1Writer` -首先,我们需要获取 `dbGroup1Reader` 和 `dbGroup1Writer` 的 ID。假设我们已经知道这些 ID(例如 `readerID` 和 `writerID`),我们可以直接使用它们。如果不知道,可以通过查询权限列表来获取。 - -#### 查询权限列表以获取 ID(可选) -```sh -curl -X GET http://localhost:8080/api/permissions -``` - -#### 创建角色 `db1RW` 并关联权限 -假设 `readerID` 是 1,`writerID` 是 2: -```sh -curl -X POST http://localhost:8080/api/roles \ --H "Content-Type: application/json" \ --d '{ - "name": "db1RW", - "description": "Read and Write access to db1 and db2" -}' - -# 获取新创建的角色 ID(假设为 roleID) -curl -X GET http://localhost:8080/api/roles - -# 为角色 `db1RW` 分配权限 `dbGroup1Reader` 和 `dbGroup1Writer` -curl -X POST http://localhost:8080/api/roles/{roleID}/permissions \ --H "Content-Type: application/json" \ --d '[ - {"permission_id": 1}, - {"permission_id": 2} -]' -``` - -### 5. 将账号 `lixiao` 绑定角色 `db1RW` -假设 `lixiao` 的用户 ID 是 1,`db1RW` 的角色 ID 是 1: -```sh -curl -X POST http://localhost:8080/api/users/{userID}/roles \ --H "Content-Type: application/json" \ --d '[ - {"role_id": 1} -]' -``` - -请根据实际情况替换 `{userID}` 和 `{roleID}` 为实际的 ID 值。 - -如果你需要自动化这个过程,可以编写一个脚本来执行这些命令,并处理响应以获取必要的 ID。 - -* diff --git a/api/routes.go b/api/routes.go index c8b8cbb..27b1576 100644 --- a/api/routes.go +++ b/api/routes.go @@ -1,55 +1,49 @@ package api import ( - "gitea.zjmud.xyz/phyer/rbac/controllers" - "gitea.zjmud.xyz/phyer/rbac/middleware" + "gitea.zjmud.com/phyer/rbac/controllers" + "gitea.zjmud.com/phyer/rbac/middleware" "github.com/gin-gonic/gin" ) -func SetupRoutes(r *gin.Engine) { - // 认证相关路由 - auth := r.Group("/auth") +func SetupRouter(db *gorm.DB, redisClient *redis.Client) *gin.Engine { + router := gin.Default() + + // Initialize controllers + authController := controllers.NewAuthController(db, redisClient) + rbacController := controllers.NewRBACController(db, redisClient) + userController := controllers.NewUserController(db, redisClient) + + // Public routes - No authentication required + authGroup := router.Group("/auth") { - auth.POST("/register", controllers.Register) - auth.POST("/login", controllers.Login) + authGroup.POST("/login", authController.Login) + authGroup.POST("/register", authController.Register) } - // 需要认证的路由 - api := r.Group("/api") - api.Use(middleware.JWTAuth()) + // Protected routes - Require JWT authentication + apiGroup := router.Group("/api") + apiGroup.Use(middleware.AuthMiddleware()) { - // 用户管理 - users := api.Group("/users") + // User management routes + userGroup := apiGroup.Group("/users") { - users.GET("/", controllers.GetUsers) - users.GET("/:id", controllers.GetUser) - users.PUT("/:id", controllers.UpdateUser) - users.DELETE("/:id", controllers.DeleteUser) + userGroup.GET("", userController.GetUsers) + userGroup.GET("/:id", userController.GetUserByID) } - // 角色管理 - roles := api.Group("/roles") + // RBAC administration routes with additional authorization + rbacGroup := apiGroup.Group("/rbac") + rbacGroup.Use(middleware.RBACMiddleware()) { - roles.POST("/", controllers.CreateRole) - roles.GET("/", controllers.GetRoles) - roles.GET("/:id", controllers.GetRole) - roles.PUT("/:id", controllers.UpdateRole) - roles.DELETE("/:id", controllers.DeleteRole) + rbacGroup.POST("/roles", rbacController.CreateRole) + rbacGroup.POST("/permissions", rbacController.CreatePermission) + rbacGroup.POST("/resources", rbacController.CreateResource) + rbacGroup.POST("/resource-groups", rbacController.CreateResourceGroup) + rbacGroup.POST("/actions", rbacController.CreateAction) + rbacGroup.POST("/assignments", rbacController.AssignRoleToUser) } - - // 权限管理 - permissions := api.Group("/permissions") - { - permissions.POST("", controllers.CreatePermission) - permissions.GET("", controllers.GetPermissions) - permissions.GET("/:id", controllers.GetPermission) - permissions.PUT("/:id", controllers.UpdatePermission) - permissions.DELETE("/:id", controllers.DeletePermission) - } - - // Handle trailing slash redirect - api.GET("/permissions/", func(c *gin.Context) { - c.Redirect(301, "/api/permissions") - }) } + + return router } diff --git a/config/config.go b/config/config.go index 6ae596d..c74fc80 100644 --- a/config/config.go +++ b/config/config.go @@ -1,61 +1,60 @@ package config import ( + "fmt" + "log" "os" - "strconv" + + "github.com/go-redis/redis/v8" + "gorm.io/driver/mysql" + "gorm.io/gorm" ) -type DBConfig struct { - Host string - Port int - User string - Password string - Name string -} - -func (db *DBConfig) DSN() string { - return db.User + ":" + db.Password + "@tcp(" + db.Host + ":" + strconv.Itoa(db.Port) + ")/" + db.Name + "?charset=utf8mb4&parseTime=True&loc=Local" -} - type Config struct { - DB DBConfig - JWTSecret string - RedisHost string - RedisPort int - RedisPassword string - RedisDB int + DBHost string + DBPort string + DBUser string + DBPassword string + DBName string + RedisAddr string + RedisPass string + RedisDB int + ServerPort string } -var AppConfig Config - -func Init() { - AppConfig = Config{ - DB: DBConfig{ - Host: getEnv("DB_HOST", "192.168.65.5"), - Port: getEnvAsInt("DB_PORT", 3306), - User: getEnv("DB_USER", "root"), - Password: getEnv("DB_PASSWORD", "d8db*#00oS"), - Name: getEnv("DB_NAME", "rbac"), - }, - JWTSecret: getEnv("JWT_SECRET", "secret"), - RedisHost: getEnv("REDIS_HOST", "192.168.65.5"), - RedisPort: getEnvAsInt("REDIS_PORT", 6379), - RedisPassword: getEnv("REDIS_PASSWORD", ""), - RedisDB: getEnvAsInt("REDIS_DB", 2), +func LoadConfig() *Config { + return &Config{ + DBHost: os.Getenv("DB_HOST"), + DBPort: os.Getenv("DB_PORT"), + DBUser: os.Getenv("DB_USER"), + DBPassword: os.Getenv("DB_PASSWORD"), + DBName: os.Getenv("DB_NAME"), + RedisAddr: os.Getenv("REDIS_ADDR"), + RedisPass: os.Getenv("REDIS_PASS"), + RedisDB: 0, + ServerPort: os.Getenv("SERVER_PORT"), } } -func getEnv(key, defaultValue string) string { - if value, exists := os.LookupEnv(key); exists { - return value +func InitDB(cfg *Config) *gorm.DB { + dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", + cfg.DBUser, cfg.DBPassword, cfg.DBHost, cfg.DBPort, cfg.DBName) + db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) + if err != nil { + log.Fatalf("Failed to connect to database: %v", err) } - return defaultValue + return db } -func getEnvAsInt(key string, defaultValue int) int { - valueStr := getEnv(key, "") - if value, err := strconv.Atoi(valueStr); err == nil { - return value +func InitRedis(cfg *Config) *redis.Client { + rdb := redis.NewClient(&redis.Options{ + Addr: cfg.RedisAddr, + Password: cfg.RedisPass, + DB: cfg.RedisDB, + }) + _, err := rdb.Ping(rdb.Context()).Result() + if err != nil { + log.Fatalf("Failed to connect to Redis: %v", err) } - return defaultValue + return rdb } diff --git a/controllers/auth.go b/controllers/auth.go index 1fa4267..1104aa9 100644 --- a/controllers/auth.go +++ b/controllers/auth.go @@ -1,42 +1,104 @@ package controllers import ( - "gitea.zjmud.xyz/phyer/rbac/services" + "fmt" + "net/http" + + "gitea.zjmud.com/phyer/rbac/models" + "gitea.zjmud.com/phyer/rbac/repositories" + "gitea.zjmud.com/phyer/rbac/utils" "github.com/gin-gonic/gin" + "github.com/go-redis/redis/v8" + "github.com/go-redis/redis/v8" + "gorm.io/gorm" ) -func Register(c *gin.Context) { - var registerData struct { - Username string `json:"username"` - Fullname string `json:"fullname"` - Password string `json:"password"` - Email string `json:"email"` - } - if err := c.ShouldBindJSON(®isterData); err != nil { - c.JSON(400, gin.H{"error": err.Error()}) - return - } - user, err := services.RegisterUser(registerData.Username, registerData.Fullname, registerData.Password, registerData.Email) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(201, user) +type AuthController struct { + DB *gorm.DB + Redis *redis.Client } -func Login(c *gin.Context) { - var loginData struct { - Username string `json:"username"` - Password string `json:"password"` - } - if err := c.ShouldBindJSON(&loginData); err != nil { - c.JSON(400, gin.H{"error": err.Error()}) - return - } - token, err := services.Login(loginData.Username, loginData.Password) - if err != nil { - c.JSON(401, gin.H{"error": err.Error()}) - return - } - c.JSON(200, gin.H{"token": token}) +func NewAuthController(db *gorm.DB, redis *redis.Client) *AuthController { + return &AuthController{DB: db, Redis: redis} +} + +func (ac *AuthController) Login(c *gin.Context) { + type LoginRequest struct { + Username string `json:"username" binding:"required"` + Password string `json:"password" binding:"required"` + } + + var req LoginRequest + if err := c.ShouldBindJSON(&req); err != nil { + utils.Error(c, http.StatusBadRequest, "Invalid request format") + return + } + + userRepo := repositories.NewUserRepository(ac.DB) + user, err := userRepo.FindByUsername(req.Username) + if err != nil { + utils.Error(c, http.StatusUnauthorized, "Invalid credentials") + return + } + + + hashedPassword, err := models.HashPassword(req.Password) + if err != nil { + utils.Error(c, http.StatusUnauthorized, "Invalid credentials") + return + } + hashedHex := fmt.Sprintf("%x", hashedPassword) + if hashedHex != user.Password { + if err != nil { + utils.Error(c, http.StatusUnauthorized, "Invalid credentials") + return + } + + hashedHex := fmt.Sprintf("%x", hashedPassword) + if hashedHex != user.Password { + utils.Error(c, http.StatusUnauthorized, "Invalid credentials") + return + } + + token, err := utils.GenerateToken(user.ID) + if err != nil { + utils.Error(c, http.StatusInternalServerError, "Failed to generate token") + return + } + + utils.Success(c, gin.H{"token": token}) +} + +func (ac *AuthController) Register(c *gin.Context) { + type RegisterRequest struct { + Username string `json:"username" binding:"required"` + Password string `json:"password" binding:"required"` + Email string `json:"email" binding:"required,email"` + } + + var req RegisterRequest + if err := c.ShouldBindJSON(&req); err != nil { + utils.Error(c, http.StatusBadRequest, "Invalid request format") + return + } + + hashedPassword, err := models.HashPassword(req.Password) + if err != nil { + utils.Error(c, http.StatusInternalServerError, "Failed to hash password") + return + } + + newUser := &models.User{ + Username: req.Username, + Password: string(hashedPassword), + Email: req.Email, + } + + userRepo := repositories.NewUserRepository(ac.DB) + if err := userRepo.Create(newUser); err != nil { + utils.Error(c, http.StatusConflict, "User already exists") + return + } + + utils.Success(c, newUser) } diff --git a/controllers/rbac.go b/controllers/rbac.go index 73cc89b..8e34ce3 100644 --- a/controllers/rbac.go +++ b/controllers/rbac.go @@ -1,228 +1,53 @@ package controllers import ( - // "log" + "net/http" - "strconv" - - "gitea.zjmud.xyz/phyer/rbac/services" + "gitea.zjmud.com/phyer/rbac/models" "github.com/gin-gonic/gin" - "github.com/sirupsen/logrus" + "gorm.io/gorm" ) -// CreateRole 创建新角色 -func CreateRole(c *gin.Context) { - var roleData struct { +type RBACController struct { + DB *gorm.DB + Redis *redis.Client +} + +func NewRBACController(db *gorm.DB, redis *redis.Client) *RBACController { + return &RBACController{DB: db, Redis: redis} +} + +func (rc *RBACController) CreateRole(c *gin.Context) { + type CreateRoleRequest struct { Name string `json:"name" binding:"required"` Description string `json:"description"` } - if err := c.ShouldBindJSON(&roleData); err != nil { - c.JSON(400, gin.H{"error": err.Error()}) + + var req CreateRoleRequest + if err := c.ShouldBindJSON(&req); err != nil { + utils.Error(c, http.StatusBadRequest, "Invalid request format") return } - role, err := services.CreateRole(roleData.Name, roleData.Description) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) + // 检查角色是否已存在 + roleRepo := repositories.NewRoleRepository(rc.DB) + exists, err := roleRepo.ExistsByNameExcludeID(req.Name, 0) + if err != nil || exists { + utils.Error(c, http.StatusConflict, "Role already exists") return } - c.JSON(201, role) + + newRole := &models.Role{ + Name: req.Name, + Description: req.Description, + } + + if err := roleRepo.Create(newRole); err != nil { + utils.Error(c, http.StatusInternalServerError, "Failed to create role") + return + } + + utils.Success(c, newRole) } -// GetRoles 获取所有角色 -func GetRoles(c *gin.Context) { - roles, err := services.GetAllRoles() - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, roles) -} - -// CreatePermission 创建新权限 -func CreatePermission(c *gin.Context) { - logrus.Info("CreatePermission called") - - var permissionData struct { - Name string `json:"name" binding:"required"` - Description string `json:"description"` - Resource string `json:"resource" binding:"required"` - Action string `json:"action" binding:"required"` - } - if err := c.ShouldBindJSON(&permissionData); err != nil { - logrus.Errorf("Error binding JSON: %v", err) - c.JSON(400, gin.H{"error": err.Error()}) - return - } - - permission, err := services.CreatePermission(permissionData.Name, permissionData.Description, permissionData.Resource, permissionData.Action) - if err != nil { - logrus.Errorf("Error creating permission: %v", err) - c.JSON(500, gin.H{"error": err.Error()}) - return - } - logrus.Infof("Permission created: %+v", permission) - c.JSON(201, permission) -} - -// AssignRoleToUser 为用户分配角色 -func AssignRoleToUser(c *gin.Context) { - var assignmentData struct { - UserID uint `json:"user_id" binding:"required"` - RoleID uint `json:"role_id" binding:"required"` - } - if err := c.ShouldBindJSON(&assignmentData); err != nil { - c.JSON(400, gin.H{"error": err.Error()}) - return - } - - err := services.AssignRoleToUser(assignmentData.UserID, assignmentData.RoleID) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, gin.H{"message": "Role assigned successfully"}) -} - -// AssignPermissionToRole 为角色分配权限 -func AssignPermissionToRole(c *gin.Context) { - var assignmentData struct { - RoleID uint `json:"role_id" binding:"required"` - PermissionID uint `json:"permission_id" binding:"required"` - } - if err := c.ShouldBindJSON(&assignmentData); err != nil { - c.JSON(400, gin.H{"error": err.Error()}) - return - } - - err := services.AssignPermissionToRole(assignmentData.RoleID, assignmentData.PermissionID) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, gin.H{"message": "Permission assigned successfully"}) -} - -// GetPermission 获取单个权限 -func GetPermission(c *gin.Context) { - id := c.Param("id") - permissionID, err := strconv.ParseUint(id, 10, 64) - if err != nil { - c.JSON(400, gin.H{"error": "Invalid permission ID"}) - return - } - - permission, err := services.GetPermissionByID(uint(permissionID)) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, permission) -} - -// UpdatePermission 更新权限 -func UpdatePermission(c *gin.Context) { - id := c.Param("id") - permissionID, err := strconv.ParseUint(id, 10, 64) - if err != nil { - c.JSON(400, gin.H{"error": "Invalid permission ID"}) - return - } - - var updateData map[string]interface{} - if err := c.ShouldBindJSON(&updateData); err != nil { - c.JSON(400, gin.H{"error": err.Error()}) - return - } - - permission, err := services.UpdatePermission(uint(permissionID), updateData) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, permission) -} - -// DeletePermission 删除权限 -func DeletePermission(c *gin.Context) { - id := c.Param("id") - permissionID, err := strconv.ParseUint(id, 10, 64) - if err != nil { - c.JSON(400, gin.H{"error": "Invalid permission ID"}) - return - } - - err = services.DeletePermission(uint(permissionID)) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, gin.H{"message": "Permission deleted successfully"}) -} - -// GetPermissions 获取所有权限 -func GetPermissions(c *gin.Context) { - permissions, err := services.GetAllPermissions() - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, permissions) -} - -// GetRole 获取单个角色 -func GetRole(c *gin.Context) { - id := c.Param("id") - roleID, err := strconv.ParseUint(id, 10, 64) - if err != nil { - c.JSON(400, gin.H{"error": "Invalid role ID"}) - return - } - - role, err := services.GetRoleByID(uint(roleID)) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, role) -} - -// UpdateRole 更新角色 -func UpdateRole(c *gin.Context) { - id := c.Param("id") - roleID, err := strconv.ParseUint(id, 10, 64) - if err != nil { - c.JSON(400, gin.H{"error": "Invalid role ID"}) - return - } - - var updateData map[string]interface{} - if err := c.ShouldBindJSON(&updateData); err != nil { - c.JSON(400, gin.H{"error": err.Error()}) - return - } - - role, err := services.UpdateRole(uint(roleID), updateData) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, role) -} - -// DeleteRole 删除角色 -func DeleteRole(c *gin.Context) { - id := c.Param("id") - roleID, err := strconv.ParseUint(id, 10, 64) - if err != nil { - c.JSON(400, gin.H{"error": "Invalid role ID"}) - return - } - - err = services.DeleteRole(uint(roleID)) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, gin.H{"message": "Role deleted successfully"}) -} +// 其他RBAC相关方法... diff --git a/controllers/user.go b/controllers/user.go index f3f3ef6..595ca5a 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -1,50 +1,37 @@ package controllers import ( - "gitea.zjmud.xyz/phyer/rbac/services" + "net/http" + + "gitea.zjmud.com/phyer/rbac/models" "github.com/gin-gonic/gin" + "gorm.io/gorm" ) -func GetUsers(c *gin.Context) { - users, err := services.GetAllUsers() - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, users) +type UserController struct { + DB *gorm.DB + Redis *redis.Client } -func GetUser(c *gin.Context) { - id := c.Param("id") - user, err := services.GetUserByID(id) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, user) +func NewUserController(db *gorm.DB, redis *redis.Client) *UserController { + return &UserController{DB: db, Redis: redis} } -func UpdateUser(c *gin.Context) { - id := c.Param("id") - var updateData map[string]interface{} - if err := c.ShouldBindJSON(&updateData); err != nil { - c.JSON(400, gin.H{"error": err.Error()}) +func (uc *UserController) GetUsers(c *gin.Context) { + var users []models.User + if err := uc.DB.Find(&users).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to fetch users"}) return } - user, err := services.UpdateUser(id, updateData) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, user) + c.JSON(http.StatusOK, users) } -func DeleteUser(c *gin.Context) { +func (uc *UserController) GetUserByID(c *gin.Context) { id := c.Param("id") - err := services.DeleteUser(id) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) + var user models.User + if err := uc.DB.First(&user, id).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "user not found"}) return } - c.JSON(200, gin.H{"message": "User deleted successfully"}) + c.JSON(http.StatusOK, user) } diff --git a/go.mod b/go.mod index 71ef948..e08f2ab 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,17 @@ -module gitea.zjmud.xyz/phyer/rbac +module gitea.zjmud.com/phyer/rbac -go 1.21 +go 1.20 require ( github.com/gin-gonic/gin v1.9.1 - github.com/go-redis/redis/v8 v8.11.5 - github.com/golang-jwt/jwt/v5 v5.0.0 - github.com/sirupsen/logrus v1.9.3 - golang.org/x/crypto v0.9.0 - gorm.io/driver/mysql v1.5.7 - gorm.io/gorm v1.25.7 + github.com/go-redis/redis/v8 v8.11.5 // 使用存在的版本 + github.com/go-sql-driver/mysql v1.7.1 // indirect + gorm.io/driver/mysql v1.5.1 + gorm.io/gorm v1.25.4 ) +require github.com/golang-jwt/jwt/v5 v5.2.1 + require ( github.com/bytedance/sonic v1.9.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect @@ -22,7 +22,6 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.14.0 // indirect - github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect @@ -36,6 +35,7 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect golang.org/x/arch v0.3.0 // indirect + golang.org/x/crypto v0.9.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect diff --git a/go.sum b/go.sum index 1067655..c3d2406 100644 --- a/go.sum +++ b/go.sum @@ -12,7 +12,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -20,7 +19,6 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= @@ -29,12 +27,13 @@ github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= -github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -58,17 +57,12 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= -github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -92,7 +86,6 @@ golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0 golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -106,14 +99,13 @@ google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= -gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= -gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A= -gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw= +gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o= +gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw= +gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/init.sh b/init.sh new file mode 100755 index 0000000..9b52af5 --- /dev/null +++ b/init.sh @@ -0,0 +1,32 @@ +# 创建所有目录 +mkdir -p api config controllers middleware models repositories server services utils + +# 创建api目录下的文件 +touch api/routes.go + +# 创建config目录下的文件 +touch config/config.go + +# 创建controllers目录下的文件 +touch controllers/auth.go controllers/rbac.go controllers/user.go + +# 创建middleware目录下的文件 +touch middleware/auth.go middleware/rbac.go + +# 创建models目录下的文件 +touch models/permission.go models/role.go models/user.go models/user_group.go + +# 创建repositories目录下的文件 +touch repositories/db.go repositories/permission.go repositories/role.go repositories/user.go repositories/user_group.go + +# 创建server目录下的文件 +touch server/server.go + +# 创建services目录下的文件 +touch services/auth.go services/rbac.go services/user.go + +# 创建utils目录下的文件 +touch utils/jwt.go utils/redis.go utils/response.go + +# 创建其他文件 +touch go.mod go.sum main.go sug.md tree.txt rbac/ diff --git a/main.go b/main.go index 45fa2fd..949165a 100644 --- a/main.go +++ b/main.go @@ -1,18 +1,28 @@ package main import ( - "gitea.zjmud.xyz/phyer/rbac/api" - "gitea.zjmud.xyz/phyer/rbac/config" - "gitea.zjmud.xyz/phyer/rbac/repositories" - "gitea.zjmud.xyz/phyer/rbac/server" + "log" + + "gitea.zjmud.com/phyer/rbac/api" + "gitea.zjmud.com/phyer/rbac/config" + "github.com/gin-gonic/gin" ) func main() { - config.Init() - if err := repositories.InitDB(); err != nil { - panic("failed to initialize database: " + err.Error()) + // 加载配置 + cfg := config.LoadConfig() + + // 初始化数据库 + db := config.InitDB(cfg) + + // 初始化Redis + redisClient := config.InitRedis(cfg) + + // 初始化路由 + router := api.SetupRouter(db, redisClient) + + // 启动服务 + if err := router.Run(":" + cfg.ServerPort); err != nil { + log.Fatalf("Failed to start server: %v", err) } - r := server.NewServer() - api.SetupRoutes(r) - r.Run(":8080") } diff --git a/middleware/auth.go b/middleware/auth.go index 9018bcd..5086a88 100644 --- a/middleware/auth.go +++ b/middleware/auth.go @@ -1,31 +1,27 @@ -package middleware +package middlewares import ( - "gitea.zjmud.xyz/phyer/rbac/utils" "github.com/gin-gonic/gin" + "net/http" ) -// JWTAuth is an alias for AuthMiddleware -func JWTAuth() gin.HandlerFunc { - return AuthMiddleware() -} - func AuthMiddleware() gin.HandlerFunc { return func(c *gin.Context) { - token := c.GetHeader("Authorization") - if token == "" { - c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"}) + tokenString := c.GetHeader("Authorization") + if tokenString == "" { + utils.Error(c, http.StatusUnauthorized, "Authorization header required") + c.Abort() return } - claims, err := utils.ParseJWT(token) + claims, err := utils.ParseToken(tokenString) if err != nil { - c.AbortWithStatusJSON(401, gin.H{"error": "Invalid token"}) + utils.Error(c, http.StatusUnauthorized, "Invalid token") + c.Abort() return } - userID := claims["user_id"].(string) - c.Set("userID", userID) + c.Set("userID", claims.UserID) c.Next() } } diff --git a/middleware/rbac.go b/middleware/rbac.go index c080df7..5214584 100644 --- a/middleware/rbac.go +++ b/middleware/rbac.go @@ -1,23 +1,31 @@ -package middleware +package middlewares import ( - "gitea.zjmud.xyz/phyer/rbac/repositories" "github.com/gin-gonic/gin" + "net/http" ) -func RBACMiddleware(permission string) gin.HandlerFunc { +func RBACMiddleware() gin.HandlerFunc { return func(c *gin.Context) { - userID := c.GetString("userID") - if userID == "" { - c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"}) + // 获取用户ID + userID, exists := c.Get("userID") + if !exists { + utils.Error(c, http.StatusForbidden, "User authentication required") + c.Abort() return } - hasPermission, err := repositories.CheckUserPermission(userID, permission) - if err != nil || !hasPermission { - c.AbortWithStatusJSON(403, gin.H{"error": "Forbidden"}) - return - } + // 获取请求方法和路径 + method := c.Request.Method + path := c.FullPath() + + // 这里需要实现RBAC检查逻辑(示例代码) + // 实际应该查询用户的角色权限是否匹配当前请求 + // if !checkPermission(userID, method, path) { + // utils.Error(c, http.StatusForbidden, "Access denied") + // c.Abort() + // return + // } c.Next() } diff --git a/models/permission.go b/models/permission.go index 24b2e2f..519740b 100644 --- a/models/permission.go +++ b/models/permission.go @@ -1,11 +1,16 @@ package models -import "gorm.io/gorm" +import "time" type Permission struct { - gorm.Model - Name string `gorm:"unique;not null"` - Description string - Resource string `gorm:"not null"` - Action string `gorm:"not null"` + ID uint `gorm:"primaryKey" json:"id"` + Name string `gorm:"uniqueIndex;size:50" json:"name"` + Description string `gorm:"size:255" json:"description"` + ResourceID uint `json:"resource_id"` + ActionID uint `json:"action_id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + + Resource Resource `gorm:"foreignKey:ResourceID"` + Action Action `gorm:"foreignKey:ActionID"` } diff --git a/models/role.go b/models/role.go index 0abaf7e..bc5b6f8 100644 --- a/models/role.go +++ b/models/role.go @@ -1,9 +1,11 @@ package models -import "gorm.io/gorm" +import "time" type Role struct { - gorm.Model - Name string `gorm:"unique;not null"` - Permissions []Permission `gorm:"many2many:role_permissions;"` + ID uint `gorm:"primaryKey" json:"id"` + Name string `gorm:"uniqueIndex;size:50" json:"name"` + Description string `gorm:"size:255" json:"description"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` } diff --git a/models/user.go b/models/user.go index 8e8a5e1..f84d22a 100644 --- a/models/user.go +++ b/models/user.go @@ -1,18 +1,35 @@ package models import ( + // "crypto/rand" + "crypto/sha256" + "fmt" + "io" "time" - - "gorm.io/gorm" ) type User struct { - gorm.Model - Username string `json:"username" gorm:"uniqueIndex;not null"` - Fullname string `json:"fullname" gorm:"not null"` - Password string `json:"-" gorm:"not null"` - Email string `json:"email" gorm:"uniqueIndex;not null"` + ID uint `gorm:"primaryKey" json:"id"` + Username string `gorm:"uniqueIndex;size:50" json:"username"` + Email string `gorm:"uniqueIndex;size:100" json:"email"` + Password string `gorm:"size:255" json:"-"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` - Roles []Role `json:"roles" gorm:"many2many:user_roles;"` +} + +// HashPassword 迁移到 utils 包更合适(可选) +func HashPassword(password string) ([]byte, error) { + h := sha256.New() + if _, err := io.WriteString(h, password); err != nil { // 修正[]byte转换 + return nil, err + } + return h.Sum(nil), nil +} + +// Validate 建议移动到 service 层(可选) +func (u *User) Validate() error { + if u.Password == "" { + return fmt.Errorf("password cannot be empty") + } + return nil } diff --git a/models/user_group.go b/models/user_group.go index 4bcf8d5..54d2559 100644 --- a/models/user_group.go +++ b/models/user_group.go @@ -1,18 +1,11 @@ package models -import ( - "time" +import "time" - "gorm.io/gorm" -) - -// UserGroup 表示用户组模型 type UserGroup struct { - gorm.Model - Name string `json:"name" gorm:"uniqueIndex;not null"` // 用户组名称 - Description string `json:"description"` // 用户组描述 - CreatedAt time.Time `json:"created_at"` // 创建时间 - UpdatedAt time.Time `json:"updated_at"` // 更新时间 - Users []User `json:"users" gorm:"many2many:user_group_users;"` // 关联的用户 - Roles []Role `json:"roles" gorm:"many2many:user_group_roles;"` // 关联的角色 + ID uint `gorm:"primaryKey" json:"id"` + Name string `gorm:"uniqueIndex;size:50" json:"name"` + Description string `gorm:"size:255" json:"description"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` } diff --git a/models/user_role.go b/models/user_role.go new file mode 100644 index 0000000..29d2f31 --- /dev/null +++ b/models/user_role.go @@ -0,0 +1,6 @@ +package models + +type UserRole struct { + UserID uint `gorm:"primaryKey" json:"user_id"` + RoleID uint `gorm:"primaryKey" json:"role_id"` +} diff --git a/repositories/db.go b/repositories/db.go index aed4fb7..1afb2cb 100644 --- a/repositories/db.go +++ b/repositories/db.go @@ -1,21 +1,53 @@ package repositories import ( - "gitea.zjmud.xyz/phyer/rbac/config" - "gitea.zjmud.xyz/phyer/rbac/models" - "gorm.io/driver/mysql" + "gitea.zjmud.com/phyer/rbac/models" "gorm.io/gorm" - // "strconv" ) -var db *gorm.DB - -func InitDB() error { - var err error - dsn := config.AppConfig.DB.DSN() - db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) - if err != nil { - return err - } - return db.AutoMigrate(&models.Permission{}, &models.Role{}, &models.UserGroup{}, &models.User{}) +// BaseRepository 提供基础CRUD操作 +type BaseRepository struct { + db *gorm.DB +} + +func NewBaseRepository(db *gorm.DB) *BaseRepository { + return &BaseRepository{db: db} +} + +// 事务支持 +func (r *BaseRepository) Begin() *gorm.DB { + return r.db.Begin() +} + +func (r *BaseRepository) Commit(tx *gorm.DB) error { + return tx.Commit().Error +} + +func (r *BaseRepository) Rollback(tx *gorm.DB) error { + return tx.Rollback().Error +} + +// 基础CRUD操作 +func (r *BaseRepository) Create(model interface{}) error { + return r.db.Create(model).Error +} + +func (r *BaseRepository) Update(model interface{}) error { + return r.db.Save(model).Error +} + +func (r *BaseRepository) Delete(model interface{}) error { + return r.db.Delete(model).Error +} + +func (r *BaseRepository) FindByID(model interface{}, id uint) error { + return r.db.First(model, id).Error +} + +func (r *BaseRepository) FindAll(models interface{}, preloads ...string) error { + tx := r.db + for _, preload := range preloads { + tx = tx.Preload(preload) + } + return tx.Find(models).Error } diff --git a/repositories/permission.go b/repositories/permission.go index 89ab88b..00b931d 100644 --- a/repositories/permission.go +++ b/repositories/permission.go @@ -1,55 +1,55 @@ package repositories import ( - "gitea.zjmud.xyz/phyer/rbac/models" + "gitea.zjmud.com/phyer/rbac/models" "gorm.io/gorm" ) -func GetPermissionByID(id uint) (*models.Permission, error) { - var permission models.Permission - result := db.First(&permission, id) - return &permission, result.Error +type PermissionRepository interface { + Create(permission *models.Permission) error + Update(permission *models.Permission) error + Delete(permission *models.Permission) error + FindByID(id uint) (*models.Permission, error) + FindAll() ([]models.Permission, error) + FindByName(name string) (*models.Permission, error) } -func GetPermissions() ([]models.Permission, error) { - var permissions []models.Permission - result := db.Find(&permissions) - return permissions, result.Error +type permissionRepository struct { + *BaseRepository } -func UpdatePermission(id uint, updateData map[string]interface{}) (*models.Permission, error) { - var permission models.Permission - result := db.Model(&permission).Where("id = ?", id).Updates(updateData) - if result.Error != nil { - return nil, result.Error +func NewPermissionRepository(db *gorm.DB) PermissionRepository { + return &permissionRepository{ + BaseRepository: NewBaseRepository(db), } - return GetPermissionByID(id) } -func CreatePermission(permission *models.Permission) (*models.Permission, error) { - result := db.Create(permission) - return permission, result.Error +func (r *permissionRepository) Create(permission *models.Permission) error { + return r.BaseRepository.Create(permission) } -func DeletePermission(id uint) error { - result := db.Delete(&models.Permission{}, id) - return result.Error +func (r *permissionRepository) Update(permission *models.Permission) error { + return r.BaseRepository.Update(permission) } -func CheckPermissionExists(name string) (bool, error) { - var count int64 - result := db.Model(&models.Permission{}).Where("name = ?", name).Count(&count) - return count > 0, result.Error +func (r *permissionRepository) Delete(permission *models.Permission) error { + return r.BaseRepository.Delete(permission) } -func AssignRoleToUser(userID uint, roleID uint) error { - user := &models.User{Model: gorm.Model{ID: userID}} - role := &models.Role{Model: gorm.Model{ID: roleID}} - return db.Model(user).Association("Roles").Append(role) +func (r *permissionRepository) FindByID(id uint) (*models.Permission, error) { + var permission models.Permission + err := r.BaseRepository.FindByID(&permission, id) + return &permission, err } -func RemoveRoleFromUser(userID uint, roleID uint) error { - user := &models.User{Model: gorm.Model{ID: userID}} - role := &models.Role{Model: gorm.Model{ID: roleID}} - return db.Model(user).Association("Roles").Delete(role) +func (r *permissionRepository) FindAll() ([]models.Permission, error) { + var permissions []models.Permission + err := r.BaseRepository.FindAll(&permissions) + return permissions, err +} + +func (r *permissionRepository) FindByName(name string) (*models.Permission, error) { + var permission models.Permission + err := r.db.Where("name = ?", name).First(&permission).Error + return &permission, err } diff --git a/repositories/role.go b/repositories/role.go index 2c142b8..b9f0e2d 100644 --- a/repositories/role.go +++ b/repositories/role.go @@ -1,59 +1,53 @@ package repositories import ( - "gitea.zjmud.xyz/phyer/rbac/models" + "gitea.zjmud.com/phyer/rbac/models" "gorm.io/gorm" ) -func CreateRole(role *models.Role) (*models.Role, error) { - result := db.Create(role) - return role, result.Error +type RoleRepository struct { + *BaseRepository } -func GetAllRoles() ([]models.Role, error) { - var roles []models.Role - result := db.Find(&roles) - return roles, result.Error -} - -func GetRoleByID(id uint) (*models.Role, error) { - var role models.Role - result := db.First(&role, id) - return &role, result.Error -} - -func CheckRoleExists(name string) (bool, error) { - var count int64 - result := db.Model(&models.Role{}).Where("name = ?", name).Count(&count) - return count > 0, result.Error -} - -func AddPermissionToRole(roleID, permissionID uint) error { - role := &models.Role{Model: gorm.Model{ID: roleID}} - permission := &models.Permission{Model: gorm.Model{ID: permissionID}} - return db.Model(role).Association("Permissions").Append(permission) -} - -func GetRoles() ([]models.Role, error) { - var roles []models.Role - result := db.Preload("Permissions").Find(&roles) - return roles, result.Error -} - -func UpdateRole(id uint, updateData map[string]interface{}) (*models.Role, error) { - var role models.Role - result := db.Model(&role).Where("id = ?", id).Updates(updateData) - if result.Error != nil { - return nil, result.Error +func NewRoleRepository(db *gorm.DB) *RoleRepository { + return &RoleRepository{ + BaseRepository: NewBaseRepository(db), } - return GetRoleByID(id) } -func DeleteRole(id uint) error { - result := db.Delete(&models.Role{}, id) - return result.Error +// 根据角色名称查找角色 +func (r *RoleRepository) FindByName(role *models.Role, name string) error { + return r.db.Where("name = ?", name).First(role).Error } -func RemovePermissionFromRole(roleID uint, permissionID uint) error { - return db.Model(&models.Role{Model: gorm.Model{ID: roleID}}).Association("Permissions").Delete(&models.Permission{Model: gorm.Model{ID: permissionID}}) +// 分页查询角色列表 +func (r *RoleRepository) FindPaged(roles *[]models.Role, page, pageSize int, preloads ...string) error { + tx := r.db.Model(&models.Role{}) + + for _, preload := range preloads { + tx = tx.Preload(preload) + } + + return tx.Scopes(func(db *gorm.DB) *gorm.DB { + return db.Offset((page - 1) * pageSize).Limit(pageSize) + }).Find(roles).Error +} + +// 带权限关联的角色查询 +func (r *RoleRepository) FindWithPermissions(role *models.Role, id uint) error { + return r.db.Preload("Permissions").First(role, id).Error +} + +// 检查角色名称是否存在(排除当前角色) +func (r *RoleRepository) ExistsByNameExcludeID(name string, excludeID uint) (bool, error) { + var count int64 + err := r.db.Model(&models.Role{}). + Where("name = ? AND id != ?", name, excludeID). + Count(&count).Error + return count > 0, err +} + +// 同步角色权限关联 +func (r *RoleRepository) SyncPermissions(role *models.Role, permissionIDs []uint) error { + return r.db.Model(role).Association("Permissions").Replace(permissionIDs) } diff --git a/repositories/user.go b/repositories/user.go index cc10cfe..1ccaad3 100644 --- a/repositories/user.go +++ b/repositories/user.go @@ -1,60 +1,128 @@ package repositories import ( - "gitea.zjmud.xyz/phyer/rbac/models" - // "gorm.io/gorm" + "gitea.zjmud.com/phyer/rbac/models" + "gorm.io/gorm" ) -func CheckUserPermission(userID string, permission string) (bool, error) { - var user models.User - result := db.Preload("Roles.Permissions").Where("id = ?", userID).First(&user) - if result.Error != nil { - return false, result.Error - } - - for _, role := range user.Roles { - for _, perm := range role.Permissions { - if perm.Name == permission { - return true, nil - } - } - } - return false, nil +type UserRepository interface { + Create(user *models.User) error + Update(user *models.User) error + Delete(user *models.User) error + FindByID(id uint) (*models.User, error) + FindAll() ([]models.User, error) + FindByUsername(username string) (*models.User, error) } -func GetAllUsers() ([]models.User, error) { +type userRepository struct { + *BaseRepository +} + +func NewUserRepository(db *gorm.DB) UserRepository { + return &userRepository{ + BaseRepository: NewBaseRepository(db), + } +} + +func (r *userRepository) Create(user *models.User) error { + return r.BaseRepository.Create(user) +} + +func (r *userRepository) Update(user *models.User) error { + return r.BaseRepository.Update(user) +} + +func (r *userRepository) Delete(user *models.User) error { + return r.BaseRepository.Delete(user) +} + +func (r *userRepository) FindByID(id uint) (*models.User, error) { + var user models.User + err := r.BaseRepository.FindByID(&user, id) + return &user, err +} + +func (r *userRepository) FindAll() ([]models.User, error) { var users []models.User - result := db.Find(&users) - return users, result.Error + err := r.BaseRepository.FindAll(&users) + return users, err } -func GetUserByID(id string) (*models.User, error) { +func (r *userRepository) FindByUsername(username string) (*models.User, error) { var user models.User - result := db.First(&user, "id = ?", id) - return &user, result.Error + err := r.db.Where("username = ?", username).First(&user).Error + return &user, err +} +package repositories + +import ( + "gitea.zjmud.com/phyer/rbac/models" + "gorm.io/gorm" +) + +type UserRepository interface { + Create(user *models.User) error + Update(user *models.User) error + Delete(user *models.User) error + FindByID(id uint) (*models.User, error) + FindAll() ([]models.User, error) + FindByUsername(username string) (*models.User, error) + GetUserRoles(userID uint) ([]models.Role, error) + AssignRole(userID uint, roleID uint) error + RemoveRole(userID uint, roleID uint) error } -func GetUserByUsername(username string) (*models.User, error) { - var user models.User - result := db.First(&user, "username = ?", username) - return &user, result.Error +type userRepository struct { + *BaseRepository } -func CreateUser(user *models.User) (*models.User, error) { - result := db.Create(user) - return user, result.Error -} - -func UpdateUser(id string, updateData map[string]interface{}) (*models.User, error) { - var user models.User - result := db.Model(&user).Where("id = ?", id).Updates(updateData) - if result.Error != nil { - return nil, result.Error +func NewUserRepository(db *gorm.DB) UserRepository { + return &userRepository{ + BaseRepository: NewBaseRepository(db), } - return GetUserByID(id) } -func DeleteUser(id string) error { - result := db.Delete(&models.User{}, "id = ?", id) - return result.Error +func (r *userRepository) Create(user *models.User) error { + return r.BaseRepository.Create(user) } + +func (r *userRepository) Update(user *models.User) error { + return r.BaseRepository.Update(user) +} + +func (r *userRepository) Delete(user *models.User) error { + return r.BaseRepository.Delete(user) +} + +func (r *userRepository) FindByID(id uint) (*models.User, error) { + var user models.User + err := r.db.Preload("Roles").First(&user, id).Error + return &user, err +} + +func (r *userRepository) FindAll() ([]models.User, error) { + var users []models.User + err := r.BaseRepository.FindAll(&users, "Roles") + return users, err +} + +func (r *userRepository) FindByUsername(username string) (*models.User, error) { + var user models.User + err := r.db.Where("username = ?", username).First(&user).Error + return &user, err +} + +func (r *userRepository) GetUserRoles(userID uint) ([]models.Role, error) { + var roles []models.Role + err := r.db.Model(&models.User{ID: userID}).Association("Roles").Find(&roles) + return roles, err +} + +func (r *userRepository) AssignRole(userID uint, roleID uint) error { + return r.db.Model(&models.User{ID: userID}).Association("Roles").Append(&models.Role{ID: roleID}) +} + +func (r *userRepository) RemoveRole(userID uint, roleID uint) error { + return r.db.Model(&models.User{ID: userID}).Association("Roles").Delete(&models.Role{ID: roleID}) +} + diff --git a/repositories/user_group.go b/repositories/user_group.go index eefc47d..3ec7d5b 100644 --- a/repositories/user_group.go +++ b/repositories/user_group.go @@ -1,61 +1,55 @@ package repositories import ( - "gitea.zjmud.xyz/phyer/rbac/models" + "gitea.zjmud.com/phyer/rbac/models" "gorm.io/gorm" ) -func CreateUserGroup(userGroup *models.UserGroup) (*models.UserGroup, error) { - result := db.Create(userGroup) - return userGroup, result.Error +type UserGroupRepository interface { + Create(group *models.UserGroup) error + Update(group *models.UserGroup) error + Delete(group *models.UserGroup) error + FindByID(id uint) (*models.UserGroup, error) + FindAll() ([]models.UserGroup, error) + FindByName(name string) (*models.UserGroup, error) } -func GetUserGroupByID(id uint) (*models.UserGroup, error) { - var userGroup models.UserGroup - result := db.Preload("Users").Preload("Roles").First(&userGroup, id) - return &userGroup, result.Error +type userGroupRepository struct { + *BaseRepository } -func GetUserGroups() ([]models.UserGroup, error) { - var userGroups []models.UserGroup - result := db.Preload("Users").Preload("Roles").Find(&userGroups) - return userGroups, result.Error -} - -func UpdateUserGroup(id uint, updateData map[string]interface{}) (*models.UserGroup, error) { - var userGroup models.UserGroup - result := db.Model(&userGroup).Where("id = ?", id).Updates(updateData) - if result.Error != nil { - return nil, result.Error +func NewUserGroupRepository(db *gorm.DB) UserGroupRepository { + return &userGroupRepository{ + BaseRepository: NewBaseRepository(db), } - return GetUserGroupByID(id) } -func DeleteUserGroup(id uint) error { - result := db.Delete(&models.UserGroup{}, id) - return result.Error +func (r *userGroupRepository) Create(group *models.UserGroup) error { + return r.BaseRepository.Create(group) } -func AddUserToGroup(userGroupID uint, userID uint) error { - return db.Model(&models.UserGroup{Model: gorm.Model{ID: userGroupID}}). - Association("Users"). - Append(&models.User{Model: gorm.Model{ID: userID}}) +func (r *userGroupRepository) Update(group *models.UserGroup) error { + return r.BaseRepository.Update(group) } -func RemoveUserFromGroup(userGroupID uint, userID uint) error { - return db.Model(&models.UserGroup{Model: gorm.Model{ID: userGroupID}}). - Association("Users"). - Delete(&models.User{Model: gorm.Model{ID: userID}}) +func (r *userGroupRepository) Delete(group *models.UserGroup) error { + return r.BaseRepository.Delete(group) } -func AddRoleToGroup(userGroupID uint, roleID uint) error { - return db.Model(&models.UserGroup{Model: gorm.Model{ID: userGroupID}}). - Association("Roles"). - Append(&models.Role{Model: gorm.Model{ID: roleID}}) +func (r *userGroupRepository) FindByID(id uint) (*models.UserGroup, error) { + var group models.UserGroup + err := r.BaseRepository.FindByID(&group, id) + return &group, err } -func RemoveRoleFromGroup(userGroupID uint, roleID uint) error { - return db.Model(&models.UserGroup{Model: gorm.Model{ID: userGroupID}}). - Association("Roles"). - Delete(&models.Role{Model: gorm.Model{ID: roleID}}) +func (r *userGroupRepository) FindAll() ([]models.UserGroup, error) { + var groups []models.UserGroup + err := r.BaseRepository.FindAll(&groups) + return groups, err +} + +func (r *userGroupRepository) FindByName(name string) (*models.UserGroup, error) { + var group models.UserGroup + err := r.db.Where("name = ?", name).First(&group).Error + return &group, err } diff --git a/server/server.go b/server/server.go index 775718d..b90662c 100644 --- a/server/server.go +++ b/server/server.go @@ -1,27 +1,16 @@ package server import ( - "gitea.zjmud.xyz/phyer/rbac/api" - // "gitea.zjmud.xyz/phyer/rbac/config" + "fmt" + "net/http" + "github.com/gin-gonic/gin" - // "gorm.io/driver/mysql" - // "gorm.io/gorm" ) -// InitDB is now handled in repositories package - -// NewServer creates and returns a new Gin server -func NewServer() *gin.Engine { - r := gin.Default() - return r -} - -func Start() { - r := gin.Default() - - // 初始化路由 - api.SetupRoutes(r) - - // 启动服务器 - r.Run(":8080") +func StartServer(router *gin.Engine, cfg *config.Config) { + addr := fmt.Sprintf(":%s", cfg.ServerPort) + fmt.Printf("Starting server on %s\n", addr) + if err := router.Run(addr); err != nil { + panic(err) + } } diff --git a/services/auth.go b/services/auth.go index f793e91..d9a9a35 100644 --- a/services/auth.go +++ b/services/auth.go @@ -1,27 +1,31 @@ package services import ( - "errors" - // "gitea.zjmud.xyz/phyer/rbac/models" - "strconv" - - "gitea.zjmud.xyz/phyer/rbac/repositories" - "gitea.zjmud.xyz/phyer/rbac/utils" - "golang.org/x/crypto/bcrypt" + "gitea.zjmud.com/phyer/rbac/models" + "gitea.zjmud.com/phyer/rbac/repositories" + "gorm.io/gorm" ) -func Login(username, password string) (string, error) { - user, err := repositories.GetUserByUsername(username) - if err != nil { - return "", errors.New("invalid credentials") - } - err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) - if err != nil { - return "", errors.New("invalid credentials") - } - token, err := utils.GenerateJWT(strconv.FormatUint(uint64(user.ID), 10)) - if err != nil { - return "", err - } - return token, nil +type AuthService struct { + userRepo repositories.UserRepository +} + +func NewAuthService(db *gorm.DB) *AuthService { + return &AuthService{ + userRepo: repositories.NewUserRepository(db), + } +} + +func (s *AuthService) Authenticate(username, password string) (*models.User, error) { + user, err := s.userRepo.FindByUsername(username) + if err != nil { + return nil, err + } + + hashedPassword, err := models.HashPassword(password) + if err != nil || string(hashedPassword) != user.Password { + return nil, ErrInvalidCredentials + } + + return user, nil } diff --git a/services/rbac.go b/services/rbac.go index ec6d485..e69de29 100644 --- a/services/rbac.go +++ b/services/rbac.go @@ -1,237 +0,0 @@ -package services - -import ( - "errors" - "strconv" - - "gitea.zjmud.xyz/phyer/rbac/models" - "gitea.zjmud.xyz/phyer/rbac/repositories" -) - -// AssignRoleToUser 为用户分配角色 -func AssignRoleToUser(userID uint, roleID uint) error { - // 检查用户是否存在 - user, err := repositories.GetUserByID(strconv.FormatUint(uint64(userID), 10)) - if err != nil { - return errors.New("user not found") - } - - // 检查角色是否存在 - role, err := repositories.GetRoleByID(roleID) - if err != nil { - return errors.New("role not found") - } - - // 分配角色 - return repositories.AssignRoleToUser(user.ID, role.ID) -} - -// RemoveRoleFromUser 移除用户的角色 -func RemoveRoleFromUser(userID uint, roleID uint) error { - // 检查用户是否存在 - user, err := repositories.GetUserByID(strconv.FormatUint(uint64(userID), 10)) - if err != nil { - return errors.New("user not found") - } - - // 检查角色是否存在 - role, err := repositories.GetRoleByID(roleID) - if err != nil { - return errors.New("role not found") - } - - // 移除角色 - return repositories.RemoveRoleFromUser(user.ID, role.ID) -} - -// CreateRoleWithPermissions 创建角色并分配权限 -func CreateRoleWithPermissions(roleName string, permissionIDs []uint) (*models.Role, error) { - // 检查角色是否已存在 - exists, err := repositories.CheckRoleExists(roleName) - if err != nil { - return nil, err - } - if exists { - return nil, errors.New("role already exists") - } - - // 创建角色 - role := &models.Role{Name: roleName} - role, err = repositories.CreateRole(role) - if err != nil { - return nil, err - } - - // 为角色分配权限 - for _, permissionID := range permissionIDs { - err = repositories.AddPermissionToRole(role.ID, permissionID) - if err != nil { - return nil, err - } - } - - return role, nil -} - -// CreateRole 创建新角色 -func CreateRole(name, description string) (*models.Role, error) { - // 检查角色是否已存在 - exists, err := repositories.CheckRoleExists(name) - if err != nil { - return nil, err - } - if exists { - return nil, errors.New("role already exists") - } - - // 创建角色 - role := &models.Role{ - Name: name, - } - return repositories.CreateRole(role) -} - -// GetAllRoles 获取所有角色 -func GetAllRoles() ([]models.Role, error) { - return repositories.GetAllRoles() -} - -// CreatePermission 创建新权限 -func CreatePermission(name, description, resource, action string) (*models.Permission, error) { - // 检查权限是否已存在 - exists, err := repositories.CheckPermissionExists(name) - if err != nil { - return nil, err - } - if exists { - return nil, errors.New("permission already exists") - } - - // 创建权限 - permission := &models.Permission{ - Name: name, - Resource: resource, - Action: action, - } - return repositories.CreatePermission(permission) -} - -// AssignPermissionToRole 为角色分配权限 -func AssignPermissionToRole(roleID, permissionID uint) error { - // 检查角色是否存在 - _, err := repositories.GetRoleByID(roleID) - if err != nil { - return errors.New("role not found") - } - - // 检查权限是否存在 - _, err = repositories.GetPermissionByID(permissionID) - if err != nil { - return errors.New("permission not found") - } - - // 分配权限 - return repositories.AddPermissionToRole(roleID, permissionID) -} - -// GetAllPermissions 获取所有权限 -func GetAllPermissions() ([]models.Permission, error) { - return repositories.GetPermissions() -} - -// GetRoleByID 根据ID获取角色 -func GetRoleByID(roleID uint) (*models.Role, error) { - return repositories.GetRoleByID(roleID) -} - -// UpdateRole 更新角色 -func UpdateRole(roleID uint, updateData map[string]interface{}) (*models.Role, error) { - // 检查角色是否存在 - _, err := repositories.GetRoleByID(roleID) - if err != nil { - return nil, errors.New("role not found") - } - - // 更新角色 - return repositories.UpdateRole(roleID, updateData) -} - -// DeleteRole 删除角色 -func DeleteRole(roleID uint) error { - // 检查角色是否存在 - _, err := repositories.GetRoleByID(roleID) - if err != nil { - return errors.New("role not found") - } - - // 删除角色 - return repositories.DeleteRole(roleID) -} - -// GetPermissionByID 根据ID获取权限 -func GetPermissionByID(permissionID uint) (*models.Permission, error) { - return repositories.GetPermissionByID(permissionID) -} - -// UpdatePermission 更新权限 -func UpdatePermission(permissionID uint, updateData map[string]interface{}) (*models.Permission, error) { - // 检查权限是否存在 - _, err := repositories.GetPermissionByID(permissionID) - if err != nil { - return nil, errors.New("permission not found") - } - - // 更新权限 - return repositories.UpdatePermission(permissionID, updateData) -} - -// DeletePermission 删除权限 -func DeletePermission(permissionID uint) error { - // 检查权限是否存在 - _, err := repositories.GetPermissionByID(permissionID) - if err != nil { - return errors.New("permission not found") - } - - // 删除权限 - return repositories.DeletePermission(permissionID) -} - -// CheckUserPermission 检查用户是否具有特定权限 -// CheckUserPermission 检查用户是否具有特定权限 -func CheckUserPermission(userID string, permission string) (bool, error) { - // 获取用户的所有角色 - user, err := repositories.GetUserByID(userID) - if err != nil { - return false, err - } - - // 检查每个角色的权限 - for _, role := range user.Roles { - for _, perm := range role.Permissions { - if perm.Name == permission { - return true, nil - } - } - } - - return false, nil -} - -// GetUserPermissions 获取用户的所有权限 -func GetUserPermissions(userID string) ([]string, error) { - var permissions []string - user, err := repositories.GetUserByID(userID) - if err != nil { - return nil, err - } - - // 收集所有角色的权限 - for _, role := range user.Roles { - for _, perm := range role.Permissions { - permissions = append(permissions, perm.Name) - } - } - - return permissions, nil -} diff --git a/services/user.go b/services/user.go index 04e1997..e69de29 100644 --- a/services/user.go +++ b/services/user.go @@ -1,46 +0,0 @@ -package services - -import ( - "gitea.zjmud.xyz/phyer/rbac/models" - "gitea.zjmud.xyz/phyer/rbac/repositories" - - // "gitea.zjmud.xyz/phyer/rbac/utils" - "golang.org/x/crypto/bcrypt" -) - -func GetAllUsers() ([]models.User, error) { - return repositories.GetAllUsers() -} - -func GetUserByID(id string) (*models.User, error) { - return repositories.GetUserByID(id) -} - -func UpdateUser(id string, updateData map[string]interface{}) (*models.User, error) { - if password, ok := updateData["password"]; ok { - hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password.(string)), bcrypt.DefaultCost) - if err != nil { - return nil, err - } - updateData["password"] = string(hashedPassword) - } - return repositories.UpdateUser(id, updateData) -} - -func DeleteUser(id string) error { - return repositories.DeleteUser(id) -} - -func RegisterUser(username, fullname, password, email string) (*models.User, error) { - hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) - if err != nil { - return nil, err - } - user := &models.User{ - Username: username, - Fullname: fullname, - Password: string(hashedPassword), - Email: email, - } - return repositories.CreateUser(user) -} diff --git a/sug.md b/sug.md index dd680f8..e69de29 100644 --- a/sug.md +++ b/sug.md @@ -1,2062 +0,0 @@ - -- Datetime: 2025-02-16 12:45:39 - -- Model: alidsv3/deepseek-v3 - -- Selected files: - - tree.txt - -> -> 我需要创建一饿go语言gin框架的api后端项目,项目名字叫 gitea.zjmud.xyz/phyer/rbac, 需要创建一个用户账号系统, 包括基础的用户验证功能和鉴权,鉴权依照rbac模式,管理员可以创建角色、用户、定义资源、资源组、和动作,管理员可以委派某个用户,对特定资源组和用户组进行动作定义, 请把go文件根据逻辑,放置到不同的模块中,不要都放到main 模块下 -> > -> > 那么当前目录结构是否需要调整, 请给出每个文件的内容 - -根据你的需求,我们需要创建一个基于 Gin 框架的 RBAC 系统。当前的目录结构基本合理,但需要做一些调整和补充。以下是建议的目录结构和每个文件的内容: - -1. 首先,我们需要调整目录结构,添加一些必要的文件和目录: - -Filepath: tree.txt -Replace lines: 1-31 -```text -. -├── api -│   └── routes.go -├── config -│   └── config.go -├── controllers -│   ├── auth.go -│   ├── rbac.go -│   └── user.go -├── go.mod -├── go.sum -├── main.go -├── middleware -│   ├── auth.go -│   └── rbac.go -├── models -│   ├── permission.go -│   ├── role.go -│   ├── user.go -│   └── user_group.go -├── repositories -│   ├── permission.go -│   ├── role.go -│   ├── user.go -│   └── user_group.go -├── server -│   └── server.go -├── services -│   ├── auth.go -│   ├── rbac.go -│   └── user.go -├── utils -│   ├── jwt.go -│   └── response.go -├── sug.md -└── tree.txt - -10 directories, 26 files -``` - -2. 现在让我们创建每个文件的内容。由于内容较多,我将分多次提供。首先从核心文件开始: - -Filepath: go.mod -Replace lines: 1-0 -```unknown -module gitea.zjmud.xyz/phyer/rbac - -go 1.21 - -require ( - github.com/gin-gonic/gin v1.9.1 - github.com/golang-jwt/jwt/v5 v5.0.0 - gorm.io/driver/mysql v1.5.1 - gorm.io/gorm v1.25.2 -) -``` - -Filepath: main.go -Replace lines: 1-0 -```go -package main - -import ( - "gitea.zjmud.xyz/phyer/rbac/config" - "gitea.zjmud.xyz/phyer/rbac/server" -) - -func main() { - // 初始化配置 - config.Init() - - // 启动服务器 - server.Start() -} -``` - -Filepath: config/config.go -Replace lines: 1-0 -```go -package config - -import ( - "os" - "strconv" -) - -type Config struct { - DBHost string - DBPort int - DBUser string - DBPassword string - DBName string - JWTSecret string -} - -var AppConfig Config - -func Init() { - AppConfig = Config{ - DBHost: getEnv("DB_HOST", "localhost"), - DBPort: getEnvAsInt("DB_PORT", 3306), - DBUser: getEnv("DB_USER", "root"), - DBPassword: getEnv("DB_PASSWORD", ""), - DBName: getEnv("DB_NAME", "rbac"), - JWTSecret: getEnv("JWT_SECRET", "secret"), - } -} - -func getEnv(key, defaultValue string) string { - if value, exists := os.LookupEnv(key); exists { - return value - } - return defaultValue -} - -func getEnvAsInt(key string, defaultValue int) int { - valueStr := getEnv(key, "") - if value, err := strconv.Atoi(valueStr); err == nil { - return value - } - return defaultValue -} -``` - -Filepath: server/server.go -Replace lines: 1-0 -```go -package server - -import ( - "gitea.zjmud.xyz/phyer/rbac/api" - "gitea.zjmud.xyz/phyer/rbac/config" - "gitea.zjmud.xyz/phyer/rbac/middleware" - "github.com/gin-gonic/gin" -) - -func Start() { - r := gin.Default() - - // 全局中间件 - r.Use(middleware.CORS()) - - // 初始化路由 - api.InitRoutes(r) - - // 启动服务器 - r.Run(":8080") -} -``` - -Filepath: api/routes.go -Replace lines: 1-0 -```go -package api - -import ( - "gitea.zjmud.xyz/phyer/rbac/controllers" - "gitea.zjmud.xyz/phyer/rbac/middleware" - "github.com/gin-gonic/gin" -) - -func InitRoutes(r *gin.Engine) { - // 公共路由 - public := r.Group("/api") - { - public.POST("/login", controllers.Login) - public.POST("/register", controllers.Register) - } - - // 需要认证的路由 - auth := r.Group("/api") - auth.Use(middleware.JWTAuth()) - { - // 用户管理 - auth.GET("/users", controllers.GetUsers) - auth.GET("/users/:id", controllers.GetUser) - auth.PUT("/users/:id", controllers.UpdateUser) - auth.DELETE("/users/:id", controllers.DeleteUser) - - // RBAC 管理 - auth.POST("/roles", controllers.CreateRole) - auth.GET("/roles", controllers.GetRoles) - auth.POST("/permissions", controllers.CreatePermission) - auth.GET("/permissions", controllers.GetPermissions) - auth.POST("/assign-role", controllers.AssignRoleToUser) - auth.POST("/assign-permission", controllers.AssignPermissionToRole) - } -} -``` - -由于内容较多,我将继续在下一个回复中提供其他文件的内容。请让我知道是否要继续。 - ---- - -- Datetime: 2025-02-16 12:50:02 - -- Model: alidsv3/deepseek-v3 - -- Selected files: - - tree.txt - -> 我重新定义一下需求: 我需要创建一饿go语言gin框架的api后端项目,项目名字叫 gitea.zjmud.xyz/phyer/rbac, 需要创建一个用户账号系统, 包括基础的用户验证功能和鉴权,鉴权依照rbac模式,管理员可以创建角色、用户、定义资源、资源组、和动作,管理员可以委派某个用户,对特定资源组和用户组进行动作定义, 请把go文件根据逻辑,放置到不同的模块中,不要都放到main 模块下 -> , 数据库使用mysql,如果有必要的话在加上redis -> 那么当前目录结构是否需要调整, 请给出每个文件的内容 -> - -根据你的新需求,我会调整目录结构并添加 Redis 支持。以下是新的目录结构和文件内容: - -1. 首先,更新目录结构: - -Filepath: tree.txt -Replace lines: 1-0 -```text -. -├── api -│ └── routes.go -├── config -│ └── config.go -├── controllers -│ ├── auth.go -│ ├── rbac.go -│ └── user.go -├── go.mod -├── go.sum -├── main.go -├── middleware -│ ├── auth.go -│ └── rbac.go -├── models -│ ├── permission.go -│ ├── role.go -│ ├── user.go -│ └── user_group.go -├── repositories -│ ├── permission.go -│ ├── role.go -│ ├── user.go -│ └── user_group.go -├── server -│ └── server.go -├── services -│ ├── auth.go -│ ├── rbac.go -│ └── user.go -├── utils -│ ├── jwt.go -│ ├── redis.go -│ └── response.go -├── sug.md -└── tree.txt - -10 directories, 27 files -``` - -2. 更新 go.mod 文件,添加 Redis 依赖: - -Filepath: go.mod -Replace lines: 1-0 -```unknown -module gitea.zjmud.xyz/phyer/rbac - -go 1.21 - -require ( - github.com/gin-gonic/gin v1.9.1 - github.com/go-redis/redis/v8 v8.11.5 - github.com/golang-jwt/jwt/v5 v5.0.0 - gorm.io/driver/mysql v1.5.1 - gorm.io/gorm v1.25.2 -) -``` - -3. 更新 config.go 添加 Redis 配置: - -Filepath: config/config.go -Replace lines: 1-0 -```go -package config - -import ( - "os" - "strconv" -) - -type Config struct { - DBHost string - DBPort int - DBUser string - DBPassword string - DBName string - JWTSecret string - RedisHost string - RedisPort int - RedisPassword string -} - -var AppConfig Config - -func Init() { - AppConfig = Config{ - DBHost: getEnv("DB_HOST", "localhost"), - DBPort: getEnvAsInt("DB_PORT", 3306), - DBUser: getEnv("DB_USER", "root"), - DBPassword: getEnv("DB_PASSWORD", ""), - DBName: getEnv("DB_NAME", "rbac"), - JWTSecret: getEnv("JWT_SECRET", "secret"), - RedisHost: getEnv("REDIS_HOST", "localhost"), - RedisPort: getEnvAsInt("REDIS_PORT", 6379), - RedisPassword: getEnv("REDIS_PASSWORD", ""), - } -} - -func getEnv(key, defaultValue string) string { - if value, exists := os.LookupEnv(key); exists { - return value - } - return defaultValue -} - -func getEnvAsInt(key string, defaultValue int) int { - valueStr := getEnv(key, "") - if value, err := strconv.Atoi(valueStr); err == nil { - return value - } - return defaultValue -} -``` - -4. 添加 Redis 工具类: - -Filepath: utils/redis.go -Replace lines: 1-0 -```go -package main - -import ( - "gitea.zjmud.xyz/phyer/rbac/config" - "gitea.zjmud.xyz/phyer/rbac/server" - "gitea.zjmud.xyz/phyer/rbac/utils" -) - -func main() { - // 初始化配置 - config.Init() - - // 初始化 Redis - utils.InitRedis() - - // 启动服务器 - server.Start() -} -``` - -6. 更新 models 文件,添加 RBAC 相关模型: - -Filepath: models/user.go - -package models - -import "gorm.io/gorm" - -type User struct { - gorm.Model - Username string `gorm:"unique;not null"` - Password string `gorm:"not null"` - Email string `gorm:"unique;not null"` - Roles []Role `gorm:"many2many:user_roles;"` - IsActive bool `gorm:"default:true"` -} -``` - -Filepath: models/role.go - -package models - -import "gorm.io/gorm" - -type Role struct { - gorm.Model - Name string `gorm:"unique;not null"` - Permissions []Permission `gorm:"many2many:role_permissions;"` -} -``` - -Filepath: models/permission.go - -package models - -import "gorm.io/gorm" - -type Permission struct { - gorm.Model - Name string `gorm:"unique;not null"` - Description string - Resource string `gorm:"not null"` - Action string `gorm:"not null"` -} -``` - -Filepath: models/user_group.go - ---- - -- Datetime: 2025-02-16 13:25:47 - -- Model: alidsv3/deepseek-v3 - -- Selected files: - - neo-tree filesystem [1] - -> 给出创建此目录结构的shell语句 -> -> . -> ├── api -> │ └── routes.go -> ├── config -> │ └── config.go -> ├── controllers -> │ ├── auth.go -> │ ├── rbac.go -> │ └── user.go -> ├── go.mod -> ├── go.sum -> ├── main.go -> ├── middleware -> │ ├── auth.go -> │ └── rbac.go -> ├── models -> │ ├── permission.go -> │ ├── role.go -> │ ├── user.go -> │ └── user_group.go -> ├── repositories -> │ ├── permission.go -> │ ├── role.go -> │ ├── user.go -> │ └── user_group.go -> ├── server -> │ └── server.go -> ├── services -> │ ├── auth.go -> │ ├── rbac.go -> │ └── user.go -> ├── utils -> │ ├── jwt.go -> │ ├── redis.go -> │ └── response.go -> ├── sug.md -> └── tree.txt -> - -以下是在当前目录下创建所需目录结构的 shell 命令: - -```zsh -mkdir -p api config controllers middleware models repositories server services utils -touch api/routes.go config/config.go controllers/{auth.go,rbac.go,user.go} go.mod go.sum main.go middleware/{auth.go,rbac.go} models/{permission.go,role.go,user.go,user_group.go} repositories/{permission.go,role.go,user.go,user_group.go} server/server.go services/{auth.go,rbac.go,user.go} utils/{jwt.go,redis.go,response.go} sug.md tree.txt -``` - -这个命令会: - -1. 使用 `mkdir -p` 递归创建所有需要的目录 -2. 使用 `touch` 创建所有需要的空文件 -3. 使用花括号 `{}` 来批量创建同一目录下的多个文件 - -执行这个命令后,你将得到所需的完整目录结构,所有文件都会被创建为空文件,等待后续填充内容。 - ---- - -- Datetime: 2025-02-16 13:27:57 - -- Model: alidsv3/deepseek-v3 - -- Selected files: - - tree.txt - -> 我需要创建一个go语言gin框架的api后端项目,项目名字叫 gitea.zjmud.xyz/phyer/rbac, 需要创建一个用户账号系统, 包括基础的用户验证功能和鉴权,鉴权依照rbac模式,管理员可以创建角色、用户、定义资源、资源组、和动作,管理员可以委派某个用户,对特定资源组和用户组进行动作定义, 请把go文件根据逻辑,放置到不同的模块中,不要都放到main 模块下, 数据库使用mysql,如果有必要的话在加上redis, 根据如下目录结构来实现上诉诉求, 然后 给出分别每个文件的内容. -> -> - -根据你的需求,我将按照提供的目录结构实现RBAC系统。以下是各个文件的内容: - -1. 首先初始化项目并添加依赖: - -Filepath: go.mod -Replace lines: 1-0 -```unknown -module gitea.zjmud.xyz/phyer/rbac - -go 1.21 - -require ( - github.com/gin-gonic/gin v1.9.1 - github.com/go-redis/redis/v8 v8.11.5 - github.com/golang-jwt/jwt/v5 v5.0.0 - gorm.io/driver/mysql v1.5.1 - gorm.io/gorm v1.25.2 -) -``` - -2. 配置文件: - -Filepath: config/config.go -Replace lines: 1-0 -```go -package config - -import ( - "os" - "strconv" -) - -type Config struct { - DBHost string - DBPort int - DBUser string - DBPassword string - DBName string - JWTSecret string - RedisHost string - RedisPort int - RedisPassword string -} - -var AppConfig Config - -func Init() { - AppConfig = Config{ - DBHost: getEnv("DB_HOST", "localhost"), - DBPort: getEnvAsInt("DB_PORT", 3306), - DBUser: getEnv("DB_USER", "root"), - DBPassword: getEnv("DB_PASSWORD", ""), - DBName: getEnv("DB_NAME", "rbac"), - JWTSecret: getEnv("JWT_SECRET", "secret"), - RedisHost: getEnv("REDIS_HOST", "localhost"), - RedisPort: getEnvAsInt("REDIS_PORT", 6379), - RedisPassword: getEnv("REDIS_PASSWORD", ""), - } -} - -func getEnv(key, defaultValue string) string { - if value, exists := os.LookupEnv(key); exists { - return value - } - return defaultValue -} - -func getEnvAsInt(key string, defaultValue int) int { - valueStr := getEnv(key, "") - if value, err := strconv.Atoi(valueStr); err == nil { - return value - } - return defaultValue -} -``` - -3. 主程序入口: - -Filepath: main.go -Replace lines: 1-0 -```go -package main - -import ( - "gitea.zjmud.xyz/phyer/rbac/config" - "gitea.zjmud.xyz/phyer/rbac/server" - "gitea.zjmud.xyz/phyer/rbac/utils" -) - -func main() { - // 初始化配置 - config.Init() - - // 初始化 Redis - utils.InitRedis() - - // 启动服务器 - server.Start() -} -``` - -4. 服务器启动文件: - -Filepath: server/server.go -Replace lines: 1-0 -```go -package server - -import ( - "gitea.zjmud.xyz/phyer/rbac/api" - "github.com/gin-gonic/gin" -) - -func Start() { - r := gin.Default() - - // 初始化路由 - api.SetupRoutes(r) - - // 启动服务器 - r.Run(":8080") -} -``` - -5. 路由配置: - -Filepath: api/routes.go -Replace lines: 1-0 -```go -package api - -import ( - "gitea.zjmud.xyz/phyer/rbac/controllers" - "gitea.zjmud.xyz/phyer/rbac/middleware" - "github.com/gin-gonic/gin" -) - -func SetupRoutes(r *gin.Engine) { - // 认证相关路由 - auth := r.Group("/auth") - { - auth.POST("/register", controllers.Register) - auth.POST("/login", controllers.Login) - } - - // 需要认证的路由 - api := r.Group("/api") - api.Use(middleware.JWTAuth()) - { - // 用户管理 - users := api.Group("/users") - { - users.GET("/", controllers.GetUsers) - users.GET("/:id", controllers.GetUser) - users.PUT("/:id", controllers.UpdateUser) - users.DELETE("/:id", controllers.DeleteUser) - } - - // 角色管理 - roles := api.Group("/roles") - { - roles.POST("/", controllers.CreateRole) - roles.GET("/", controllers.GetRoles) - roles.GET("/:id", controllers.GetRole) - roles.PUT("/:id", controllers.UpdateRole) - roles.DELETE("/:id", controllers.DeleteRole) - } - - // 权限管理 - permissions := api.Group("/permissions") - { - permissions.POST("/", controllers.CreatePermission) - permissions.GET("/", controllers.GetPermissions) - permissions.GET("/:id", controllers.GetPermission) - permissions.PUT("/:id", controllers.UpdatePermission) - permissions.DELETE("/:id", controllers.DeletePermission) - } - } -} -``` - -由于内容较多,我将继续在下一个回复中提供控制器、服务层、仓库层和中间件的实现。请让我知道是否要继续。 - ---- - -- Datetime: 2025-02-16 17:36:11 - -- Model: alidsv3/deepseek-v3 - -- Selected files: - - config/config.go - - tree.txt - -> 好的,请继续 - -好的,我将继续提供其他模块的实现代码。以下是控制器、服务层、仓库层和中间件的实现: - -1. 用户控制器: - -Filepath: controllers/user.go -Replace lines: 1-0 -```go -package controllers - -import ( - "gitea.zjmud.xyz/phyer/rbac/services" - "github.com/gin-gonic/gin" -) - -func GetUsers(c *gin.Context) { - users, err := services.GetAllUsers() - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, users) -} - -func GetUser(c *gin.Context) { - id := c.Param("id") - user, err := services.GetUserByID(id) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, user) -} - -func UpdateUser(c *gin.Context) { - id := c.Param("id") - var updateData map[string]interface{} - if err := c.ShouldBindJSON(&updateData); err != nil { - c.JSON(400, gin.H{"error": err.Error()}) - return - } - user, err := services.UpdateUser(id, updateData) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, user) -} - -func DeleteUser(c *gin.Context) { - id := c.Param("id") - err := services.DeleteUser(id) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, gin.H{"message": "User deleted successfully"}) -} -``` - -2. 认证控制器: - -Filepath: controllers/auth.go -Replace lines: 1-0 -```go -package controllers - -import ( - "gitea.zjmud.xyz/phyer/rbac/services" - "github.com/gin-gonic/gin" -) - -func Register(c *gin.Context) { - var registerData struct { - Username string `json:"username"` - Password string `json:"password"` - Email string `json:"email"` - } - if err := c.ShouldBindJSON(®isterData); err != nil { - c.JSON(400, gin.H{"error": err.Error()}) - return - } - user, err := services.RegisterUser(registerData.Username, registerData.Password, registerData.Email) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(201, user) -} - -func Login(c *gin.Context) { - var loginData struct { - Username string `json:"username"` - Password string `json:"password"` - } - if err := c.ShouldBindJSON(&loginData); err != nil { - c.JSON(400, gin.H{"error": err.Error()}) - return - } - token, err := services.Login(loginData.Username, loginData.Password) - if err != nil { - c.JSON(401, gin.H{"error": err.Error()}) - return - } - c.JSON(200, gin.H{"token": token}) -} -``` - -3. 用户服务层: - -Filepath: services/user.go -Replace lines: 1-0 -```go -package services - -import ( - "gitea.zjmud.xyz/phyer/rbac/models" - "gitea.zjmud.xyz/phyer/rbac/repositories" - "gitea.zjmud.xyz/phyer/rbac/utils" - "golang.org/x/crypto/bcrypt" -) - -func GetAllUsers() ([]models.User, error) { - return repositories.GetAllUsers() -} - -func GetUserByID(id string) (*models.User, error) { - return repositories.GetUserByID(id) -} - -func UpdateUser(id string, updateData map[string]interface{}) (*models.User, error) { - if password, ok := updateData["password"]; ok { - hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password.(string)), bcrypt.DefaultCost) - if err != nil { - return nil, err - } - updateData["password"] = string(hashedPassword) - } - return repositories.UpdateUser(id, updateData) -} - -func DeleteUser(id string) error { - return repositories.DeleteUser(id) -} - -func RegisterUser(username, password, email string) (*models.User, error) { - hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) - if err != nil { - return nil, err - } - user := &models.User{ - Username: username, - Password: string(hashedPassword), - Email: email, - } - return repositories.CreateUser(user) -} -``` - -4. 认证服务层: - -Filepath: services/auth.go -Replace lines: 1-0 -```go -package services - -import ( - "errors" - "gitea.zjmud.xyz/phyer/rbac/models" - "gitea.zjmud.xyz/phyer/rbac/repositories" - "gitea.zjmud.xyz/phyer/rbac/utils" - "golang.org/x/crypto/bcrypt" -) - -func Login(username, password string) (string, error) { - user, err := repositories.GetUserByUsername(username) - if err != nil { - return "", errors.New("invalid credentials") - } - err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) - if err != nil { - return "", errors.New("invalid credentials") - } - token, err := utils.GenerateJWT(user.ID) - if err != nil { - return "", err - } - return token, nil -} -``` - -5. JWT工具类: - -Filepath: utils/jwt.go -Replace lines: 1-0 -```go -package utils - -import ( - "time" - "gitea.zjmud.xyz/phyer/rbac/config" - "github.com/golang-jwt/jwt/v5" -) - -func GenerateJWT(userID string) (string, error) { - claims := jwt.MapClaims{ - "user_id": userID, - "exp": time.Now().Add(time.Hour * 24).Unix(), - } - token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - return token.SignedString([]byte(config.AppConfig.JWTSecret)) -} - -func ParseJWT(tokenString string) (jwt.MapClaims, error) { - token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { - return []byte(config.AppConfig.JWTSecret), nil - }) - if err != nil { - return nil, err - } - if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { - return claims, nil - } - return nil, errors.New("invalid token") -} -``` - -由于内容较多,我将继续在下一个回复中提供模型、仓库层和中间件的实现。请让我知道是否要继续。 - ---- - -- Datetime: 2025-02-16 17:48:07 - -- Model: alidsv3/deepseek-v3 - -- Selected files: - - config/config.go - - tree.txt - -> 好的,请继续 -> - -好的,我将继续提供模型、仓库层和中间件的实现代码: - -1. 用户模型: - -Filepath: models/user.go -Replace lines: 1-0 -```go -package models - -import ( - "time" -) - -type User struct { - ID string `json:"id" gorm:"primaryKey"` - Username string `json:"username" gorm:"uniqueIndex;not null"` - Password string `json:"-" gorm:"not null"` - Email string `json:"email" gorm:"uniqueIndex;not null"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Roles []Role `json:"roles" gorm:"many2many:user_roles;"` -} -``` - -2. 用户仓库层: - -Filepath: repositories/user.go -Replace lines: 1-0 -```go -package repositories - -import ( - "gitea.zjmud.xyz/phyer/rbac/config" - "gitea.zjmud.xyz/phyer/rbac/models" - "gorm.io/gorm" -) - -var db *gorm.DB - -func InitDB() error { - var err error - db, err = gorm.Open(mysql.Open(config.GetDSN()), &gorm.Config{}) - if err != nil { - return err - } - return db.AutoMigrate(&models.User{}) -} - -func GetAllUsers() ([]models.User, error) { - var users []models.User - result := db.Find(&users) - return users, result.Error -} - -func GetUserByID(id string) (*models.User, error) { - var user models.User - result := db.First(&user, "id = ?", id) - return &user, result.Error -} - -func GetUserByUsername(username string) (*models.User, error) { - var user models.User - result := db.First(&user, "username = ?", username) - return &user, result.Error -} - -func CreateUser(user *models.User) (*models.User, error) { - result := db.Create(user) - return user, result.Error -} - -func UpdateUser(id string, updateData map[string]interface{}) (*models.User, error) { - var user models.User - result := db.Model(&user).Where("id = ?", id).Updates(updateData) - if result.Error != nil { - return nil, result.Error - } - return GetUserByID(id) -} - -func DeleteUser(id string) error { - result := db.Delete(&models.User{}, "id = ?", id) - return result.Error -} -``` - -3. 认证中间件: - -Filepath: middleware/auth.go -Replace lines: 1-0 -```go -package middleware - -import ( - "gitea.zjmud.xyz/phyer/rbac/utils" - "github.com/gin-gonic/gin" -) - -func AuthMiddleware() gin.HandlerFunc { - return func(c *gin.Context) { - token := c.GetHeader("Authorization") - if token == "" { - c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"}) - return - } - - claims, err := utils.ParseJWT(token) - if err != nil { - c.AbortWithStatusJSON(401, gin.H{"error": "Invalid token"}) - return - } - - userID := claims["user_id"].(string) - c.Set("userID", userID) - c.Next() - } -} -``` - -4. RBAC中间件: - -Filepath: middleware/rbac.go -Replace lines: 1-0 -```go -package middleware - -import ( - "gitea.zjmud.xyz/phyer/rbac/repositories" - "github.com/gin-gonic/gin" -) - -func RBACMiddleware(permission string) gin.HandlerFunc { - return func(c *gin.Context) { - userID := c.GetString("userID") - if userID == "" { - c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"}) - return - } - - hasPermission, err := repositories.CheckUserPermission(userID, permission) - if err != nil || !hasPermission { - c.AbortWithStatusJSON(403, gin.H{"error": "Forbidden"}) - return - } - - c.Next() - } -} -``` - -5. 路由配置: - -Filepath: api/routes.go -Replace lines: 1-0 -```go -package api - -import ( - "gitea.zjmud.xyz/phyer/rbac/controllers" - "gitea.zjmud.xyz/phyer/rbac/middleware" - "github.com/gin-gonic/gin" -) - -func SetupRoutes(r *gin.Engine) { - auth := r.Group("/auth") - { - auth.POST("/register", controllers.Register) - auth.POST("/login", controllers.Login) - } - - api := r.Group("/api") - api.Use(middleware.AuthMiddleware()) - { - users := api.Group("/users") - { - users.GET("/", controllers.GetUsers) - users.GET("/:id", controllers.GetUser) - users.PUT("/:id", controllers.UpdateUser) - users.DELETE("/:id", controllers.DeleteUser) - } - } -} -``` - -6. 主程序入口: - -Filepath: main.go -Replace lines: 1-0 -```go -package main - -import ( - "gitea.zjmud.xyz/phyer/rbac/api" - "gitea.zjmud.xyz/phyer/rbac/config" - "gitea.zjmud.xyz/phyer/rbac/server" -) - -func main() { - config.Init() - server.InitDB() - r := server.NewServer() - api.SetupRoutes(r) - r.Run(":8080") -} -``` - -这些代码实现了RBAC系统的主要功能模块。请注意,这只是一个基础实现,实际生产环境中可能需要添加更多功能,如日志记录、错误处理、性能优化等。同时,数据库连接和配置管理部分可能需要根据具体环境进行调整。 - -如果你需要其他模块的实现或有任何问题,请随时告诉我。 - ---- - -- Datetime: 2025-02-16 18:12:44 - -- Model: alidsv3/deepseek-v3 - -- Selected files: - - sug.md - -> models/user_group.go 需要什么内容 -> - -`models/user_group.go` 文件应该定义用户组模型,用于将用户分组管理。以下是该文件的内容建议: - -Filepath: models/user_group.go -Replace lines: 1-0 -```go -package models - -import ( - "time" - "gorm.io/gorm" -) - -// UserGroup 表示用户组模型 -type UserGroup struct { - gorm.Model - Name string `json:"name" gorm:"uniqueIndex;not null"` // 用户组名称 - Description string `json:"description"` // 用户组描述 - CreatedAt time.Time `json:"created_at"` // 创建时间 - UpdatedAt time.Time `json:"updated_at"` // 更新时间 - Users []User `json:"users" gorm:"many2many:user_group_users;"` // 关联的用户 - Roles []Role `json:"roles" gorm:"many2many:user_group_roles;"` // 关联的角色 -} - -// UserGroupUser 表示用户和用户组的关联表 -type UserGroupUser struct { - UserID uint `gorm:"primaryKey"` // 用户ID - UserGroupID uint `gorm:"primaryKey"` // 用户组ID -} - -// UserGroupRole 表示用户组和角色的关联表 -type UserGroupRole struct { - UserGroupID uint `gorm:"primaryKey"` // 用户组ID - RoleID uint `gorm:"primaryKey"` // 角色ID -} -``` - -这个模型包含以下主要部分: - -1. `UserGroup` 结构体: - - 包含用户组的基本信息(名称、描述) - - 使用 `gorm.Model` 包含默认字段(ID, CreatedAt, UpdatedAt, DeletedAt) - - 通过 `many2many` 关联用户和角色 - -2. `UserGroupUser` 结构体: - - 用于表示用户和用户组的多对多关系 - - 作为关联表,包含复合主键(UserID 和 UserGroupID) - -3. `UserGroupRole` 结构体: - - 用于表示用户组和角色的多对多关系 - - 作为关联表,包含复合主键(UserGroupID 和 RoleID) - -这个模型设计允许: -- 将用户分组管理 -- 为整个用户组分配角色 -- 灵活地管理用户和用户组之间的关系 -- 跟踪用户组的创建和更新时间 - -你可以根据需要扩展这个模型,例如添加更多字段或关联关系。如果需要实现具体的业务逻辑,还需要在 repositories 和 services 层添加相应的代码。 - ---- - -- Datetime: 2025-02-16 18:16:15 - -- Model: alidsv3/deepseek-v3 - -- Selected files: - - sug.md - - models/permission.go - -> repositories/permission.go 这个文件用得上吗, 现在有一个 models/permission.gorm - -是的,`repositories/permission.go` 文件是需要的。它负责处理与权限相关的数据库操作,而 `models/permission.go` 只是定义了权限的数据结构。让我们创建 `repositories/permission.go` 文件: - -Filepath: repositories/permission.go -Replace lines: 1-0 -```go -package repositories - -import ( - "gitea.zjmud.xyz/phyer/rbac/models" - "gorm.io/gorm" -) - -func CreatePermission(permission *models.Permission) (*models.Permission, error) { - result := db.Create(permission) - return permission, result.Error -} - -func GetPermissionByID(id uint) (*models.Permission, error) { - var permission models.Permission - result := db.First(&permission, id) - return &permission, result.Error -} - -func GetPermissions() ([]models.Permission, error) { - var permissions []models.Permission - result := db.Find(&permissions) - return permissions, result.Error -} - -func UpdatePermission(id uint, updateData map[string]interface{}) (*models.Permission, error) { - var permission models.Permission - result := db.Model(&permission).Where("id = ?", id).Updates(updateData) - if result.Error != nil { - return nil, result.Error - } - return GetPermissionByID(id) -} - -func DeletePermission(id uint) error { - result := db.Delete(&models.Permission{}, id) - return result.Error -} - -func CheckPermissionExists(name string) (bool, error) { - var count int64 - result := db.Model(&models.Permission{}).Where("name = ?", name).Count(&count) - return count > 0, result.Error -} -``` - -这个文件实现了以下功能: - -1. `CreatePermission`: 创建新的权限 -2. `GetPermissionByID`: 根据ID获取权限 -3. `GetPermissions`: 获取所有权限列表 -4. `UpdatePermission`: 更新权限信息 -5. `DeletePermission`: 删除权限 -6. `CheckPermissionExists`: 检查权限名称是否已存在 - -这些方法将与 `models/permission.go` 中定义的 `Permission` 结构体一起工作,为权限管理提供完整的CRUD操作。 - -你还需要在 `repositories` 包中初始化数据库连接,可以在 `repositories/db.go` 中添加: - -Filepath: repositories/db.go -Replace lines: 1-0 -```go -package repositories - -import ( - "gitea.zjmud.xyz/phyer/rbac/config" - "gorm.io/driver/mysql" - "gorm.io/gorm" -) - -var db *gorm.DB - -func InitDB() error { - var err error - dsn := config.AppConfig.DBUser + ":" + config.AppConfig.DBPassword + "@tcp(" + config.AppConfig.DBHost + ":" + strconv.Itoa(config.AppConfig.DBPort) + ")/" + config.AppConfig.DBName + "?charset=utf8mb4&parseTime=True&loc=Local" - db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) - if err != nil { - return err - } - return db.AutoMigrate(&models.Permission{}) -} -``` - -这样,权限相关的数据库操作就完整了。你可以在服务层调用这些方法来处理业务逻辑。 - ---- - -- Datetime: 2025-02-16 18:25:33 - -- Model: alidsv3/deepseek-v3 - -- Selected files: - - tree.txt - -> repositories/role.go 需要吗 - -是的,`repositories/role.go` 是需要的。它负责处理与角色相关的数据库操作,与 `models/role.go` 配合使用。让我们创建这个文件: - -Filepath: repositories/role.go -Replace lines: 1-0 -```go -package repositories - -import ( - "gitea.zjmud.xyz/phyer/rbac/models" - "gorm.io/gorm" -) - -func CreateRole(role *models.Role) (*models.Role, error) { - result := db.Create(role) - return role, result.Error -} - -func GetRoleByID(id uint) (*models.Role, error) { - var role models.Role - result := db.Preload("Permissions").First(&role, id) - return &role, result.Error -} - -func GetRoles() ([]models.Role, error) { - var roles []models.Role - result := db.Preload("Permissions").Find(&roles) - return roles, result.Error -} - -func UpdateRole(id uint, updateData map[string]interface{}) (*models.Role, error) { - var role models.Role - result := db.Model(&role).Where("id = ?", id).Updates(updateData) - if result.Error != nil { - return nil, result.Error - } - return GetRoleByID(id) -} - -func DeleteRole(id uint) error { - result := db.Delete(&models.Role{}, id) - return result.Error -} - -func CheckRoleExists(name string) (bool, error) { - var count int64 - result := db.Model(&models.Role{}).Where("name = ?", name).Count(&count) - return count > 0, result.Error -} - -func AddPermissionToRole(roleID uint, permissionID uint) error { - return db.Model(&models.Role{Model: gorm.Model{ID: roleID}}).Association("Permissions").Append(&models.Permission{Model: gorm.Model{ID: permissionID}}) -} - -func RemovePermissionFromRole(roleID uint, permissionID uint) error { - return db.Model(&models.Role{Model: gorm.Model{ID: roleID}}).Association("Permissions").Delete(&models.Permission{Model: gorm.Model{ID: permissionID}}) -} -``` - -这个文件实现了以下功能: - -1. `CreateRole`: 创建新角色 -2. `GetRoleByID`: 根据ID获取角色(包含关联的权限) -3. `GetRoles`: 获取所有角色列表(包含关联的权限) -4. `UpdateRole`: 更新角色信息 -5. `DeleteRole`: 删除角色 -6. `CheckRoleExists`: 检查角色名称是否已存在 -7. `AddPermissionToRole`: 为角色添加权限 -8. `RemovePermissionFromRole`: 从角色中移除权限 - -这些方法将与 `models/role.go` 中定义的 `Role` 结构体一起工作,为角色管理提供完整的CRUD操作。 - -你还需要在 `repositories/db.go` 中添加对 `Role` 模型的自动迁移: - -Filepath: repositories/db.go -Replace lines: 1-0 -```go -package repositories - -import ( - "gitea.zjmud.xyz/phyer/rbac/config" - "gorm.io/driver/mysql" - "gorm.io/gorm" -) - -var db *gorm.DB - -func InitDB() error { - var err error - dsn := config.AppConfig.DBUser + ":" + config.AppConfig.DBPassword + "@tcp(" + config.AppConfig.DBHost + ":" + strconv.Itoa(config.AppConfig.DBPort) + ")/" + config.AppConfig.DBName + "?charset=utf8mb4&parseTime=True&loc=Local" - db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) - if err != nil { - return err - } - return db.AutoMigrate(&models.Permission{}, &models.Role{}) -} -``` - -这样,角色相关的数据库操作就完整了。你可以在服务层调用这些方法来处理业务逻辑。 - ---- - -- Datetime: 2025-02-16 18:36:07 - -- Model: alidsv3/deepseek-v3 - -- Selected files: - - sug.md - -> repositories/user_group.go 需要什么内容 - -`repositories/user_group.go` 文件用于处理与用户组相关的数据库操作。以下是该文件的内容建议: - -Filepath: repositories/user_group.go -Replace lines: 1-0 -```go -package repositories - -import ( - "gitea.zjmud.xyz/phyer/rbac/models" - "gorm.io/gorm" -) - -func CreateUserGroup(userGroup *models.UserGroup) (*models.UserGroup, error) { - result := db.Create(userGroup) - return userGroup, result.Error -} - -func GetUserGroupByID(id uint) (*models.UserGroup, error) { - var userGroup models.UserGroup - result := db.Preload("Users").Preload("Roles").First(&userGroup, id) - return &userGroup, result.Error -} - -func GetUserGroups() ([]models.UserGroup, error) { - var userGroups []models.UserGroup - result := db.Preload("Users").Preload("Roles").Find(&userGroups) - return userGroups, result.Error -} - -func UpdateUserGroup(id uint, updateData map[string]interface{}) (*models.UserGroup, error) { - var userGroup models.UserGroup - result := db.Model(&userGroup).Where("id = ?", id).Updates(updateData) - if result.Error != nil { - return nil, result.Error - } - return GetUserGroupByID(id) -} - -func DeleteUserGroup(id uint) error { - result := db.Delete(&models.UserGroup{}, id) - return result.Error -} - -func AddUserToGroup(userGroupID uint, userID uint) error { - return db.Model(&models.UserGroup{Model: gorm.Model{ID: userGroupID}}). - Association("Users"). - Append(&models.User{Model: gorm.Model{ID: userID}}) -} - -func RemoveUserFromGroup(userGroupID uint, userID uint) error { - return db.Model(&models.UserGroup{Model: gorm.Model{ID: userGroupID}}). - Association("Users"). - Delete(&models.User{Model: gorm.Model{ID: userID}}) -} - -func AddRoleToGroup(userGroupID uint, roleID uint) error { - return db.Model(&models.UserGroup{Model: gorm.Model{ID: userGroupID}}). - Association("Roles"). - Append(&models.Role{Model: gorm.Model{ID: roleID}}) -} - -func RemoveRoleFromGroup(userGroupID uint, roleID uint) error { - return db.Model(&models.UserGroup{Model: gorm.Model{ID: userGroupID}}). - Association("Roles"). - Delete(&models.Role{Model: gorm.Model{ID: roleID}}) -} -``` - -这个文件实现了以下功能: - -1. `CreateUserGroup`: 创建新用户组 -2. `GetUserGroupByID`: 根据ID获取用户组(包含关联的用户和角色) -3. `GetUserGroups`: 获取所有用户组列表(包含关联的用户和角色) -4. `UpdateUserGroup`: 更新用户组信息 -5. `DeleteUserGroup`: 删除用户组 -6. `AddUserToGroup`: 添加用户到用户组 -7. `RemoveUserFromGroup`: 从用户组中移除用户 -8. `AddRoleToGroup`: 为用户组添加角色 -9. `RemoveRoleFromGroup`: 从用户组中移除角色 - -这些方法将与 `models/user_group.go` 中定义的 `UserGroup` 结构体一起工作,为用户组管理提供完整的CRUD操作。 - -你还需要在 `repositories/db.go` 中添加对 `UserGroup` 模型的自动迁移: - -Filepath: repositories/db.go -Replace lines: 1-0 -```go -package repositories - -import ( - "gitea.zjmud.xyz/phyer/rbac/config" - "gorm.io/driver/mysql" - "gorm.io/gorm" -) - -var db *gorm.DB - -func InitDB() error { - var err error - dsn := config.AppConfig.DBUser + ":" + config.AppConfig.DBPassword + "@tcp(" + config.AppConfig.DBHost + ":" + strconv.Itoa(config.AppConfig.DBPort) + ")/" + config.AppConfig.DBName + "?charset=utf8mb4&parseTime=True&loc=Local" - db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) - if err != nil { - return err - } - return db.AutoMigrate(&models.Permission{}, &models.Role{}, &models.UserGroup{}) -} -``` - -这样,用户组相关的数据库操作就完整了。你可以在服务层调用这些方法来处理业务逻辑。 - ---- - -- Datetime: 2025-02-16 18:40:54 - -- Model: alidsv3/deepseek-v3 - -- Selected files: - - sug.md - -> services/rbac.go 需要什么内容 -> - -`services/rbac.go` 文件应该包含与RBAC(基于角色的访问控制)相关的业务逻辑。以下是该文件的内容建议: - -Filepath: services/rbac.go -Replace lines: 1-0 -```go -package services - -import ( - "errors" - "gitea.zjmud.xyz/phyer/rbac/models" - "gitea.zjmud.xyz/phyer/rbac/repositories" -) - -// AssignRoleToUser 为用户分配角色 -func AssignRoleToUser(userID string, roleID uint) error { - // 检查用户是否存在 - user, err := repositories.GetUserByID(userID) - if err != nil { - return errors.New("user not found") - } - - // 检查角色是否存在 - role, err := repositories.GetRoleByID(roleID) - if err != nil { - return errors.New("role not found") - } - - // 分配角色 - return repositories.AssignRoleToUser(user.ID, role.ID) -} - -// RemoveRoleFromUser 移除用户的角色 -func RemoveRoleFromUser(userID string, roleID uint) error { - // 检查用户是否存在 - user, err := repositories.GetUserByID(userID) - if err != nil { - return errors.New("user not found") - } - - // 检查角色是否存在 - role, err := repositories.GetRoleByID(roleID) - if err != nil { - return errors.New("role not found") - } - - // 移除角色 - return repositories.RemoveRoleFromUser(user.ID, role.ID) -} - -// CreateRoleWithPermissions 创建角色并分配权限 -func CreateRoleWithPermissions(roleName string, permissionIDs []uint) (*models.Role, error) { - // 检查角色是否已存在 - exists, err := repositories.CheckRoleExists(roleName) - if err != nil { - return nil, err - } - if exists { - return nil, errors.New("role already exists") - } - - // 创建角色 - role := &models.Role{Name: roleName} - role, err = repositories.CreateRole(role) - if err != nil { - return nil, err - } - - // 为角色分配权限 - for _, permissionID := range permissionIDs { - err = repositories.AddPermissionToRole(role.ID, permissionID) - if err != nil { - return nil, err - } - } - - return role, nil -} - -// CheckUserPermission 检查用户是否具有特定权限 -func CheckUserPermission(userID string, permission string) (bool, error) { - // 获取用户的所有角色 - user, err := repositories.GetUserByID(userID) - if err != nil { - return false, err - } - - // 检查每个角色的权限 - for _, role := range user.Roles { - for _, perm := range role.Permissions { - if perm.Name == permission { - return true, nil - } - } - } - - return false, nil -} - -// GetUserPermissions 获取用户的所有权限 -func GetUserPermissions(userID string) ([]string, error) { - var permissions []string - user, err := repositories.GetUserByID(userID) - if err != nil { - return nil, err - } - - // 收集所有角色的权限 - for _, role := range user.Roles { - for _, perm := range role.Permissions { - permissions = append(permissions, perm.Name) - } - } - - return permissions, nil -} -``` - -这个文件实现了以下主要功能: - -1. `AssignRoleToUser`: 为用户分配角色 -2. `RemoveRoleFromUser`: 移除用户的角色 -3. `CreateRoleWithPermissions`: 创建角色并为其分配权限 -4. `CheckUserPermission`: 检查用户是否具有特定权限 -5. `GetUserPermissions`: 获取用户的所有权限 - -这些方法将作为RBAC系统的核心业务逻辑,被控制器层调用。它们处理了角色分配、权限检查等关键功能,同时通过调用仓库层的方法与数据库进行交互。 - -你可以根据具体需求扩展这个文件,例如添加更多与RBAC相关的业务逻辑,或者优化现有的功能实现。 - ---- - -- Datetime: 2025-02-16 18:43:54 - -- Model: alidsv3/deepseek-v3 - -- Selected files: - - sug.md - -> controllers/rbac.go 需要什么内容 -> - -`controllers/rbac.go` 文件应该包含与RBAC(基于角色的访问控制)相关的控制器逻辑。以下是该文件的内容建议: - -Filepath: controllers/rbac.go -Replace lines: 1-0 -```go -package controllers - -import ( - "gitea.zjmud.xyz/phyer/rbac/services" - "github.com/gin-gonic/gin" -) - -// CreateRole 创建新角色 -func CreateRole(c *gin.Context) { - var roleData struct { - Name string `json:"name" binding:"required"` - Description string `json:"description"` - } - if err := c.ShouldBindJSON(&roleData); err != nil { - c.JSON(400, gin.H{"error": err.Error()}) - return - } - - role, err := services.CreateRole(roleData.Name, roleData.Description) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(201, role) -} - -// GetRoles 获取所有角色 -func GetRoles(c *gin.Context) { - roles, err := services.GetAllRoles() - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, roles) -} - -// CreatePermission 创建新权限 -func CreatePermission(c *gin.Context) { - var permissionData struct { - Name string `json:"name" binding:"required"` - Description string `json:"description"` - Resource string `json:"resource" binding:"required"` - Action string `json:"action" binding:"required"` - } - if err := c.ShouldBindJSON(&permissionData); err != nil { - c.JSON(400, gin.H{"error": err.Error()}) - return - } - - permission, err := services.CreatePermission(permissionData.Name, permissionData.Description, permissionData.Resource, permissionData.Action) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(201, permission) -} - -// AssignRoleToUser 为用户分配角色 -func AssignRoleToUser(c *gin.Context) { - var assignmentData struct { - UserID uint `json:"user_id" binding:"required"` - RoleID uint `json:"role_id" binding:"required"` - } - if err := c.ShouldBindJSON(&assignmentData); err != nil { - c.JSON(400, gin.H{"error": err.Error()}) - return - } - - err := services.AssignRoleToUser(assignmentData.UserID, assignmentData.RoleID) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, gin.H{"message": "Role assigned successfully"}) -} - -// AssignPermissionToRole 为角色分配权限 -func AssignPermissionToRole(c *gin.Context) { - var assignmentData struct { - RoleID uint `json:"role_id" binding:"required"` - PermissionID uint `json:"permission_id" binding:"required"` - } - if err := c.ShouldBindJSON(&assignmentData); err != nil { - c.JSON(400, gin.H{"error": err.Error()}) - return - } - - err := services.AssignPermissionToRole(assignmentData.RoleID, assignmentData.PermissionID) - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, gin.H{"message": "Permission assigned successfully"}) -} - -// GetPermissions 获取所有权限 -func GetPermissions(c *gin.Context) { - permissions, err := services.GetAllPermissions() - if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) - return - } - c.JSON(200, permissions) -} -``` - -这个控制器文件实现了以下主要功能: - -1. `CreateRole`: 创建新角色 -2. `GetRoles`: 获取所有角色列表 -3. `CreatePermission`: 创建新权限 -4. `AssignRoleToUser`: 为用户分配角色 -5. `AssignPermissionToRole`: 为角色分配权限 -6. `GetPermissions`: 获取所有权限列表 - -每个控制器方法都: -- 解析请求数据 -- 调用相应的服务层方法 -- 处理错误并返回适当的HTTP响应 - -这些控制器方法将与 `services/rbac.go` 中的服务层方法配合使用,完成RBAC系统的核心功能。 - -你还需要在 `services/rbac.go` 中实现相应的服务层方法,以及在 `repositories` 包中实现数据库操作。如果需要这些文件的内容,请告诉我。 - ---- - -- Datetime: 2025-02-16 18:47:16 - -- Model: alidsv3/deepseek-v3 - -- Selected files: - - sug.md - -> utils/redis.go 需要什么内容 -> - -`utils/redis.go` 文件用于初始化和管理 Redis 连接,并提供一些常用的 Redis 操作工具函数。以下是该文件的内容建议: - -Filepath: utils/redis.go -Replace lines: 1-0 -```go -package utils - -import ( - "context" - "fmt" - "time" - "gitea.zjmud.xyz/phyer/rbac/config" - "github.com/go-redis/redis/v8" -) - -var ( - RedisClient *redis.Client - ctx = context.Background() -) - -// InitRedis 初始化 Redis 连接 -func InitRedis() error { - RedisClient = redis.NewClient(&redis.Options{ - Addr: fmt.Sprintf("%s:%d", config.AppConfig.RedisHost, config.AppConfig.RedisPort), - Password: config.AppConfig.RedisPassword, - DB: 0, // 使用默认数据库 - }) - - // 测试连接 - _, err := RedisClient.Ping(ctx).Result() - if err != nil { - return fmt.Errorf("failed to connect to Redis: %v", err) - } - - return nil -} - -// Set 设置键值对,带过期时间 -func Set(key string, value interface{}, expiration time.Duration) error { - return RedisClient.Set(ctx, key, value, expiration).Err() -} - -// Get 获取键值 -func Get(key string) (string, error) { - return RedisClient.Get(ctx, key).Result() -} - -// Delete 删除键 -func Delete(key string) error { - return RedisClient.Del(ctx, key).Err() -} - -// Exists 检查键是否存在 -func Exists(key string) (bool, error) { - result, err := RedisClient.Exists(ctx, key).Result() - return result == 1, err -} - -// Incr 自增键值 -func Incr(key string) (int64, error) { - return RedisClient.Incr(ctx, key).Result() -} - -// Decr 自减键值 -func Decr(key string) (int64, error) { - return RedisClient.Decr(ctx, key).Result() -} - -// HSet 设置哈希字段值 -func HSet(key string, field string, value interface{}) error { - return RedisClient.HSet(ctx, key, field, value).Err() -} - -// HGet 获取哈希字段值 -func HGet(key string, field string) (string, error) { - return RedisClient.HGet(ctx, key, field).Result() -} - -// HGetAll 获取哈希所有字段值 -func HGetAll(key string) (map[string]string, error) { - return RedisClient.HGetAll(ctx, key).Result() -} - -// Expire 设置键的过期时间 -func Expire(key string, expiration time.Duration) error { - return RedisClient.Expire(ctx, key, expiration).Err() -} -``` - -这个文件主要包含以下功能: - -1. `InitRedis`: 初始化 Redis 连接,使用配置文件中的 Redis 连接信息 -2. `Set/Get`: 基本的键值对操作 -3. `Delete`: 删除键 -4. `Exists`: 检查键是否存在 -5. `Incr/Decr`: 自增/自减操作 -6. `HSet/HGet/HGetAll`: 哈希表操作 -7. `Expire`: 设置键的过期时间 - -使用示例: - -```go -// 初始化 Redis -if err := utils.InitRedis(); err != nil { - log.Fatalf("Failed to initialize Redis: %v", err) -} - -// 设置值 -err := utils.Set("mykey", "myvalue", time.Minute*10) -if err != nil { - log.Printf("Failed to set Redis key: %v", err) -} - -// 获取值 -value, err := utils.Get("mykey") -if err != nil { - log.Printf("Failed to get Redis key: %v", err) -} else { - log.Printf("Got value: %s", value) -} -``` - -这个 Redis 工具类可以用于缓存、会话管理、限流等场景。你可以根据需要扩展更多 Redis 操作函数。 - ---- - -- Datetime: 2025-02-16 18:51:26 - -- Model: alidsv3/deepseek-v3 - -- Selected files: - - sug.md - -> utils/response.go 这个文件需要吗 -> - -是的,`utils/response.go` 文件是很有用的。它可以帮助我们统一 API 的响应格式,使代码更整洁和一致。让我们创建这个文件: - -Filepath: utils/response.go -Replace lines: 1-0 -```go -package utils - -import ( - "github.com/gin-gonic/gin" -) - -// SuccessResponse 成功响应结构 -type SuccessResponse struct { - Code int `json:"code"` - Message string `json:"message"` - Data interface{} `json:"data"` -} - -// ErrorResponse 错误响应结构 -type ErrorResponse struct { - Code int `json:"code"` - Message string `json:"message"` -} - -// RespondSuccess 返回成功响应 -func RespondSuccess(c *gin.Context, code int, message string, data interface{}) { - c.JSON(code, SuccessResponse{ - Code: code, - Message: message, - Data: data, - }) -} - -// RespondError 返回错误响应 -func RespondError(c *gin.Context, code int, message string) { - c.JSON(code, ErrorResponse{ - Code: code, - Message: message, - }) -} - -// RespondValidationError 返回验证错误响应 -func RespondValidationError(c *gin.Context, errors map[string]string) { - c.JSON(400, gin.H{ - "code": 400, - "message": "Validation error", - "errors": errors, - }) -} -``` - -这个文件提供了以下功能: - -1. `SuccessResponse` 结构体:用于统一成功响应的格式 -2. `ErrorResponse` 结构体:用于统一错误响应的格式 -3. `RespondSuccess` 函数:用于返回成功的 API 响应 -4. `RespondError` 函数:用于返回错误的 API 响应 -5. `RespondValidationError` 函数:专门用于返回验证错误的响应 - -使用示例: - -```go -// 成功响应 -utils.RespondSuccess(c, http.StatusOK, "User created successfully", user) - -// 错误响应 -utils.RespondError(c, http.StatusBadRequest, "Invalid input data") - -// 验证错误响应 -utils.RespondValidationError(c, map[string]string{ - "username": "Username is required", - "email": "Invalid email format", -}) -``` - -这个工具文件可以帮助我们: -1. 保持 API 响应格式的一致性 -2. 减少重复代码 -3. 更容易维护和修改响应格式 -4. 提供更清晰的错误信息 - -建议在控制器中统一使用这些响应函数,而不是直接使用 `c.JSON()`,这样可以确保整个项目的 API 响应格式一致。 - - diff --git a/utils/jwt.go b/utils/jwt.go index 15bced7..cbdbbea 100644 --- a/utils/jwt.go +++ b/utils/jwt.go @@ -1,31 +1,38 @@ package utils import ( - "errors" "time" - "gitea.zjmud.xyz/phyer/rbac/config" "github.com/golang-jwt/jwt/v5" ) -func GenerateJWT(userID string) (string, error) { - claims := jwt.MapClaims{ - "user_id": userID, - "exp": time.Now().Add(time.Hour * 24).Unix(), - } - token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - return token.SignedString([]byte(config.AppConfig.JWTSecret)) +var JWTSecret = []byte("your-secret-key") + +type Claims struct { + UserID uint `json:"user_id"` + jwt.RegisteredClaims } -func ParseJWT(tokenString string) (jwt.MapClaims, error) { - token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { - return []byte(config.AppConfig.JWTSecret), nil - }) - if err != nil { - return nil, err +func GenerateToken(userID uint) (string, error) { + expirationTime := time.Now().Add(24 * time.Hour) + claims := &Claims{ + UserID: userID, + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(expirationTime), + }, } - if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + return token.SignedString(JWTSecret) +} + +func ParseToken(tokenString string) (*Claims, error) { + token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) { + return JWTSecret, nil + }) + + if claims, ok := token.Claims.(*Claims); ok && token.Valid { return claims, nil } - return nil, errors.New("invalid token") + return nil, err } diff --git a/utils/redis.go b/utils/redis.go index f1958a0..e69de29 100644 --- a/utils/redis.go +++ b/utils/redis.go @@ -1,83 +0,0 @@ -package utils - -import ( - "context" - "fmt" - "time" - - "gitea.zjmud.xyz/phyer/rbac/config" - "github.com/go-redis/redis/v8" -) - -var ( - RedisClient *redis.Client - ctx = context.Background() -) - -// InitRedis 初始化 Redis 连接 -func InitRedis() error { - RedisClient = redis.NewClient(&redis.Options{ - Addr: fmt.Sprintf("%s:%d", config.AppConfig.RedisHost, config.AppConfig.RedisPort), - Password: config.AppConfig.RedisPassword, - DB: config.AppConfig.RedisDB, // 使用默认数据库 - }) - - // 测试连接 - _, err := RedisClient.Ping(ctx).Result() - if err != nil { - return fmt.Errorf("failed to connect to Redis: %v", err) - } - - return nil -} - -// Set 设置键值对,带过期时间 -func Set(key string, value interface{}, expiration time.Duration) error { - return RedisClient.Set(ctx, key, value, expiration).Err() -} - -// Get 获取键值 -func Get(key string) (string, error) { - return RedisClient.Get(ctx, key).Result() -} - -// Delete 删除键 -func Delete(key string) error { - return RedisClient.Del(ctx, key).Err() -} - -// Exists 检查键是否存在 -func Exists(key string) (bool, error) { - result, err := RedisClient.Exists(ctx, key).Result() - return result == 1, err -} - -// Incr 自增键值 -func Incr(key string) (int64, error) { - return RedisClient.Incr(ctx, key).Result() -} - -// Decr 自减键值 -func Decr(key string) (int64, error) { - return RedisClient.Decr(ctx, key).Result() -} - -// HSet 设置哈希字段值 -func HSet(key string, field string, value interface{}) error { - return RedisClient.HSet(ctx, key, field, value).Err() -} - -// HGet 获取哈希字段值 -func HGet(key string, field string) (string, error) { - return RedisClient.HGet(ctx, key, field).Result() -} - -// HGetAll 获取哈希所有字段值 -func HGetAll(key string) (map[string]string, error) { - return RedisClient.HGetAll(ctx, key).Result() -} - -// Expire 设置键的过期时间 -func Expire(key string, expiration time.Duration) error { - return RedisClient.Expire(ctx, key, expiration).Err() -} diff --git a/utils/response.go b/utils/response.go index 9d0b87d..f38e49a 100644 --- a/utils/response.go +++ b/utils/response.go @@ -1,44 +1,25 @@ package utils -import ( - "github.com/gin-gonic/gin" -) +import "github.com/gin-gonic/gin" -// SuccessResponse 成功响应结构 -type SuccessResponse struct { +type Response struct { Code int `json:"code"` Message string `json:"message"` Data interface{} `json:"data"` } -// ErrorResponse 错误响应结构 -type ErrorResponse struct { - Code int `json:"code"` - Message string `json:"message"` -} - -// RespondSuccess 返回成功响应 -func RespondSuccess(c *gin.Context, code int, message string, data interface{}) { - c.JSON(code, SuccessResponse{ - Code: code, - Message: message, +func Success(c *gin.Context, data interface{}) { + c.JSON(200, Response{ + Code: 200, + Message: "success", Data: data, }) } -// RespondError 返回错误响应 -func RespondError(c *gin.Context, code int, message string) { - c.JSON(code, ErrorResponse{ +func Error(c *gin.Context, code int, message string) { + c.JSON(code, Response{ Code: code, Message: message, - }) -} - -// RespondValidationError 返回验证错误响应 -func RespondValidationError(c *gin.Context, errors map[string]string) { - c.JSON(400, gin.H{ - "code": 400, - "message": "Validation error", - "errors": errors, + Data: nil, }) }