up
This commit is contained in:
parent
fb86af7a77
commit
6669be1923
@ -1,27 +1,77 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
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("/login", controllers.Login)
|
||||
auth.POST("/register", controllers.Register)
|
||||
auth.POST("/login", controllers.Login)
|
||||
}
|
||||
|
||||
admin := r.Group("/admin")
|
||||
admin.Use(middleware.RBACMiddleware("admin", "full-access"))
|
||||
// 需要认证的路由
|
||||
api := r.Group("/api")
|
||||
api.Use(middleware.JWTAuth())
|
||||
{
|
||||
admin.POST("/roles", controllers.CreateRole)
|
||||
admin.POST("/users", controllers.CreateUser)
|
||||
admin.POST("/permissions", controllers.CreatePermission)
|
||||
admin.POST("/user-groups", controllers.CreateUserGroup)
|
||||
admin.POST("/assign-role", controllers.AssignRoleToUser)
|
||||
admin.POST("/assign-permission", controllers.AssignPermissionToRole)
|
||||
// 用户管理
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,57 +1,49 @@
|
||||
package controllers
|
||||
package config
|
||||
|
||||
import (
|
||||
"gitea.zjmud.xyz/phyer/rbac/models"
|
||||
"gitea.zjmud.xyz/phyer/rbac/services"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
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(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := services.Authenticate(loginData.Username, loginData.Password)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})
|
||||
return
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"sub": user.ID,
|
||||
"exp": time.Now().Add(time.Hour * 24).Unix(),
|
||||
})
|
||||
|
||||
tokenString, err := token.SignedString([]byte("your-256-bit-secret"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"token": tokenString})
|
||||
type Config struct {
|
||||
DBHost string
|
||||
DBPort int
|
||||
DBUser string
|
||||
DBPassword string
|
||||
DBName string
|
||||
JWTSecret string
|
||||
RedisHost string
|
||||
RedisPort int
|
||||
RedisPassword string
|
||||
}
|
||||
|
||||
func Register(c *gin.Context) {
|
||||
var user models.User
|
||||
if err := c.ShouldBindJSON(&user); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
var AppConfig Config
|
||||
|
||||
if err := services.CreateUser(&user); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
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", ""),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, user)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -1,57 +1,41 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"gitea.zjmud.xyz/phyer/rbac/models"
|
||||
"gitea.zjmud.xyz/phyer/rbac/services"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
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(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
c.JSON(400, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := services.Authenticate(loginData.Username, loginData.Password)
|
||||
token, err := services.Login(loginData.Username, loginData.Password)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})
|
||||
c.JSON(401, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"sub": user.ID,
|
||||
"exp": time.Now().Add(time.Hour * 24).Unix(),
|
||||
})
|
||||
|
||||
tokenString, err := token.SignedString([]byte("your-256-bit-secret"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"token": tokenString})
|
||||
}
|
||||
|
||||
func Register(c *gin.Context) {
|
||||
var user models.User
|
||||
if err := c.ShouldBindJSON(&user); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := services.CreateUser(&user); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, user)
|
||||
c.JSON(200, gin.H{"token": token})
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"gitea.zjmud.xyz/phyer/rbac/models"
|
||||
"gitea.zjmud.xyz/phyer/rbac/services"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func CreateRole(c *gin.Context) {
|
||||
var role models.Role
|
||||
if err := c.ShouldBindJSON(&role); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := services.CreateRole(&role); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, role)
|
||||
}
|
@ -1,24 +1,50 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"gitea.zjmud.xyz/phyer/rbac/models"
|
||||
"gitea.zjmud.xyz/phyer/rbac/services"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func CreateUser(c *gin.Context) {
|
||||
var user models.User
|
||||
if err := c.ShouldBindJSON(&user); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
func GetUsers(c *gin.Context) {
|
||||
users, err := services.GetAllUsers()
|
||||
if err != nil {
|
||||
c.JSON(500, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := services.CreateUser(&user); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, user)
|
||||
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"})
|
||||
}
|
||||
|
37
go.mod
37
go.mod
@ -4,39 +4,8 @@ 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/sqlite v1.5.4
|
||||
gorm.io/gorm v1.25.5
|
||||
github.com/caarlos0/env/v10 v10.0.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
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.20.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
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gorm.io/driver/mysql v1.5.1
|
||||
gorm.io/gorm v1.25.2
|
||||
)
|
||||
|
97
go.sum
97
go.sum
@ -1,97 +0,0 @@
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
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=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
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.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
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=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
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/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
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/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=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
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/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/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
31
main.go
31
main.go
@ -1,7 +1,34 @@
|
||||
package main
|
||||
|
||||
import "gitea.zjmud.xyz/phyer/rbac/server"
|
||||
import (
|
||||
"gitea.zjmud.xyz/phyer/rbac/api"
|
||||
"gitea.zjmud.xyz/phyer/rbac/config"
|
||||
"gitea.zjmud.xyz/phyer/rbac/server"
|
||||
)
|
||||
|
||||
func main() {
|
||||
server.Start()
|
||||
config.Init()
|
||||
server.InitDB()
|
||||
r := server.NewServer()
|
||||
api.SetupRoutes(r)
|
||||
r.Run(":8080")
|
||||
}
|
||||
|
||||
// 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()
|
||||
// }
|
||||
|
@ -1,42 +1,26 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gitea.zjmud.xyz/phyer/rbac/config"
|
||||
"net/http"
|
||||
|
||||
"gitea.zjmud.xyz/phyer/rbac/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
func JWTAuthMiddleware() gin.HandlerFunc {
|
||||
cfg := config.LoadConfig()
|
||||
func AuthMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
tokenString := c.GetHeader("Authorization")
|
||||
if tokenString == "" {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Authorization header missing"})
|
||||
token := c.GetHeader("Authorization")
|
||||
if token == "" {
|
||||
c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"})
|
||||
return
|
||||
}
|
||||
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
return []byte(cfg.JWTSecret), nil
|
||||
})
|
||||
|
||||
if err != nil || !token.Valid {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
|
||||
claims, err := utils.ParseJWT(token)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(401, gin.H{"error": "Invalid token"})
|
||||
return
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token claims"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("userID", claims["sub"])
|
||||
userID := claims["user_id"].(string)
|
||||
c.Set("userID", userID)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
24
middleware/rbac.go
Normal file
24
middleware/rbac.go
Normal file
@ -0,0 +1,24 @@
|
||||
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()
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package models
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type User struct {
|
||||
gorm.Model
|
||||
Username string `gorm:"unique"`
|
||||
Password string
|
||||
Roles []Role `gorm:"many2many:user_roles;"`
|
||||
}
|
||||
|
||||
type Role struct {
|
||||
gorm.Model
|
||||
Name string `gorm:"unique"`
|
||||
Permissions []Permission `gorm:"many2many:role_permissions;"`
|
||||
}
|
||||
|
||||
type Permission struct {
|
||||
gorm.Model
|
||||
Name string `gorm:"unique"`
|
||||
}
|
||||
|
||||
type UserGroup struct {
|
||||
gorm.Model
|
||||
Name string `gorm:"unique"`
|
||||
Users []User `gorm:"many2many:user_group_users;"`
|
||||
}
|
||||
`
|
0
models/permission.go
Normal file
0
models/permission.go
Normal file
0
models/role.go
Normal file
0
models/role.go
Normal file
15
models/user.go
Normal file
15
models/user.go
Normal file
@ -0,0 +1,15 @@
|
||||
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;"`
|
||||
}
|
0
models/user_group.go
Normal file
0
models/user_group.go
Normal file
@ -1,10 +0,0 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"gitea.zjmud.xyz/phyer/rbac/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func CreatePermission(permission *models.Permission) error {
|
||||
return db.Create(permission).Error
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"gitea.zjmud.xyz/phyer/rbac/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func CreateRole(role *models.Role) error {
|
||||
return db.Create(role).Error
|
||||
}
|
@ -1,16 +1,55 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"gitea.zjmud.xyz/phyer/rbac/config"
|
||||
"gitea.zjmud.xyz/phyer/rbac/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func GetUserByUsername(username string) (*models.User, error) {
|
||||
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.Where("username = ?", username).First(&user)
|
||||
result := db.First(&user, "id = ?", id)
|
||||
return &user, result.Error
|
||||
}
|
||||
|
||||
func CreateUser(user *models.User) error {
|
||||
return db.Create(user).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
|
||||
}
|
||||
|
@ -1,10 +0,0 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"gitea.zjmud.xyz/phyer/rbac/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func CreateUserGroup(group *models.UserGroup) error {
|
||||
return db.Create(group).Error
|
||||
}
|
@ -2,17 +2,15 @@ package server
|
||||
|
||||
import (
|
||||
"gitea.zjmud.xyz/phyer/rbac/api"
|
||||
"gitea.zjmud.xyz/phyer/rbac/config"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Start() {
|
||||
cfg := config.LoadConfig()
|
||||
|
||||
r := gin.Default()
|
||||
|
||||
// 初始化路由
|
||||
api.SetupRoutes(r)
|
||||
|
||||
r.Run(":" + cfg.ServerPort)
|
||||
// 启动服务器
|
||||
r.Run(":8080")
|
||||
}
|
||||
|
@ -4,29 +4,22 @@ 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 Authenticate(username, password string) (*models.User, error) {
|
||||
func Login(username, password string) (string, error) {
|
||||
user, err := repositories.GetUserByUsername(username)
|
||||
if err != nil {
|
||||
return nil, errors.New("user not found")
|
||||
return "", errors.New("invalid credentials")
|
||||
}
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
|
||||
return nil, errors.New("invalid password")
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func CreateUser(user *models.User) error {
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
|
||||
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))
|
||||
if err != nil {
|
||||
return err
|
||||
return "", errors.New("invalid credentials")
|
||||
}
|
||||
|
||||
user.Password = string(hashedPassword)
|
||||
return repositories.CreateUser(user)
|
||||
token, err := utils.GenerateJWT(user.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
@ -1,12 +0,0 @@
|
||||
package services
|
||||
|
||||
import "gitea.zjmud.xyz/phyer/rbac/models"
|
||||
|
||||
func CreateRole(role *models.Role) error {
|
||||
return repositories.CreateRole(role)
|
||||
}
|
||||
|
||||
func AssignRoleToUser(userID uint, roleID uint) error {
|
||||
// 实现角色分配逻辑
|
||||
return nil
|
||||
}
|
@ -1,7 +1,44 @@
|
||||
package services
|
||||
|
||||
import "gitea.zjmud.xyz/phyer/rbac/models"
|
||||
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 CreateUser(user *models.User) error {
|
||||
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)
|
||||
}
|
||||
|
475
sug.md
475
sug.md
@ -1,475 +0,0 @@
|
||||
Filepath: api/admin.go
|
||||
|
||||
---
|
||||
|
||||
- Datetime: 2025-02-16 02:30:16
|
||||
|
||||
- Model: ollamav3/chsword/DeepSeek-V3
|
||||
|
||||
- Selected files:
|
||||
- main.go
|
||||
- api/routes.go
|
||||
- middleware/auth.go
|
||||
- config/config.go
|
||||
- models/models.go
|
||||
|
||||
> router里动作对应的service,请给出实现
|
||||
>
|
||||
|
||||
To implement the service for the router's actions, we need to create a new file `services.py` in the project root directory `/Users/zhangkun/data/go/test/testai`.
|
||||
|
||||
Here are the *SEARCH/REPLACE* blocks:
|
||||
|
||||
Filepath: main.go
|
||||
Replace lines: 0-0
|
||||
```go
|
||||
import "services"
|
||||
|
||||
router.HandleFunc("/action1", services.action1Handler)
|
||||
router.HandleFunc("/action2", services.action2Handler)
|
||||
```
|
||||
|
||||
Filepath: services.go
|
||||
|
||||
---
|
||||
|
||||
- Datetime: 2025-02-16 02:32:35
|
||||
|
||||
- Model: ollamav3/chsword/DeepSeek-V3
|
||||
|
||||
- Selected files:
|
||||
- main.go
|
||||
- api/routes.go
|
||||
- middleware/auth.go
|
||||
- config/config.go
|
||||
- models/models.go
|
||||
|
||||
> admin.POST("/roles", CreateRole)
|
||||
> admin.POST("/users", CreateUser)
|
||||
> admin.POST("/permissions", CreatePermission)
|
||||
> admin.POST("/user-groups", CreateUserGroup)
|
||||
> admin.POST("/assign-role", AssignRoleToUser)
|
||||
> admin.POST("/assign-permission", AssignPermissionToRole)
|
||||
> 这里的 动作都是未定义的,需要你给出定义
|
||||
|
||||
To make these actions defined, we need to add the necessary functions and routes. Here's an example of how you can define them:
|
||||
|
||||
```markdown
|
||||
# Define the CreateRole function
|
||||
def CreateRole(request):
|
||||
# Get the role data from the request body
|
||||
role_data = request.get_json()
|
||||
|
||||
# Create a new Role object with the provided data
|
||||
role = Role(name=role_data['name'], description=role_data['description'])
|
||||
|
||||
# Save the role to the database
|
||||
db.session.add(role)
|
||||
db.session.commit()
|
||||
|
||||
# Return the created role as JSON
|
||||
return jsonify({'id': role.id, 'name': role.name, 'description': role.description})
|
||||
|
||||
# Define the CreateUser function
|
||||
def CreateUser(request):
|
||||
# Get the user data from the request body
|
||||
user_data = request.get_json()
|
||||
|
||||
# Create a new User object with the provided data
|
||||
user = User(username=user_data['username'], email=user_data['email'])
|
||||
|
||||
# Hash the password using bcrypt
|
||||
user.password = bcrypt.hashpw(user.password.encode('utf-8'), bcrypt.gensalt())
|
||||
|
||||
# Save the user to the database
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
||||
# Return the created user as JSON
|
||||
return jsonify({'id': user.id, 'username': user.username, 'email': user.email})
|
||||
|
||||
# Define the CreatePermission function
|
||||
def CreatePermission(request):
|
||||
# Get the permission data from the request body
|
||||
permission_data = request.get_json()
|
||||
|
||||
# Create a new Permission object with the provided data
|
||||
permission = Permission(name=permission_data['name'], description=permission_data['description'])
|
||||
|
||||
# Save the permission to the database
|
||||
db.session.add(permission)
|
||||
db.session.commit()
|
||||
|
||||
# Return the created permission as JSON
|
||||
return jsonify({'id': permission.id, 'name': permission.name, 'description': permission.description})
|
||||
|
||||
# Define the CreateUserGroup function
|
||||
def CreateUserGroup(request):
|
||||
# Get the group data from the request body
|
||||
group_data = request.get_json()
|
||||
|
||||
# Create a new Group object with the provided data
|
||||
group = Group(name=group_data['name'], description=group_data['description'])
|
||||
|
||||
# Save the group to the database
|
||||
db.session.add(group)
|
||||
db.session.commit()
|
||||
|
||||
# Return the created group as JSON
|
||||
return jsonify({'id': group.id, 'name': group.name, 'description': group.description})
|
||||
|
||||
# Define the AssignRoleToUser function
|
||||
def AssignRoleToUser(request):
|
||||
# Get the user ID and role ID from the request body
|
||||
user_id = request.get_json()['user_id']
|
||||
role_id = request.get_json()['role_id']
|
||||
|
||||
# Find the user and role objects in the database
|
||||
user = User.query.get(user_id)
|
||||
role = Role.query.get(role_id)
|
||||
|
||||
# Assign the role to the user
|
||||
user.roles.append(role)
|
||||
|
||||
# Save the changes to the database
|
||||
db.session.commit()
|
||||
|
||||
# Return a success message as JSON
|
||||
return jsonify({'message': 'Role assigned successfully'})
|
||||
|
||||
# Define the AssignPermissionToRole function
|
||||
def AssignPermissionToRole(request):
|
||||
# Get the role ID and permission ID from the request body
|
||||
role_id = request.get_json()['role_id']
|
||||
permission_id = request.get_json()['permission_id']
|
||||
|
||||
# Find the role and permission objects in the database
|
||||
role = Role.query.get(role_id)
|
||||
permission = Permission.query.get(permission_id)
|
||||
|
||||
# Assign the permission to the role
|
||||
role.permissions.append(permission)
|
||||
|
||||
# Save the changes to the database
|
||||
db.session.commit()
|
||||
|
||||
# Return a success message as JSON
|
||||
return jsonify({'message': 'Permission assigned successfully'})
|
||||
```
|
||||
|
||||
Note that these functions assume you have already defined the necessary models (e.g. `User`, `Role`, `Group`, etc.) and database sessions.
|
||||
|
||||
Also, I've used Flask's built-in support for JSON data in request bodies to simplify the code. If your application uses a different framework or requires more complex handling of request bodies, you may need to modify these functions accordingly.
|
||||
|
||||
To implement these routes, you would add them to your Flask app like so:
|
||||
|
||||
```python
|
||||
from flask import Flask, jsonify, request
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# ... (other routes and code)
|
||||
|
||||
admin = Blueprint('admin', __name__, url_prefix='/admin')
|
||||
|
||||
@admin.route('/roles', methods=['POST'])
|
||||
def CreateRole():
|
||||
# Call the CreateRole function defined above
|
||||
return CreateRole(request)
|
||||
|
||||
@admin.route('/users', methods=['POST'])
|
||||
def CreateUser():
|
||||
# Call the CreateUser function defined above
|
||||
return CreateUser(request)
|
||||
|
||||
@admin.route('/permissions', methods=['POST'])
|
||||
def CreatePermission():
|
||||
# Call the CreatePermission function defined above
|
||||
return CreatePermission(request)
|
||||
|
||||
@admin.route('/user-groups', methods=['POST'])
|
||||
def CreateUserGroup():
|
||||
# Call the CreateUserGroup function defined above
|
||||
return CreateUserGroup(request)
|
||||
|
||||
@admin.route('/assign-role', methods=['POST'])
|
||||
def AssignRoleToUser():
|
||||
# Call the AssignRoleToUser function defined above
|
||||
return AssignRoleToUser(request)
|
||||
|
||||
@admin.route('/assign-permission', methods=['POST'])
|
||||
def AssignPermissionToRole():
|
||||
# Call the AssignPermissionToRole function defined above
|
||||
return AssignPermissionToRole(request)
|
||||
```
|
||||
|
||||
This assumes you have already created a Blueprint for your admin interface and registered these routes with it.
|
||||
|
||||
---
|
||||
|
||||
- Datetime: 2025-02-16 02:34:18
|
||||
|
||||
- Model: ollamav3/chsword/DeepSeek-V3
|
||||
|
||||
- Selected files:
|
||||
- main.go
|
||||
- api/routes.go
|
||||
- middleware/auth.go
|
||||
- config/config.go
|
||||
- models/models.go
|
||||
|
||||
> 这不是python,这是go的gin框架
|
||||
>
|
||||
|
||||
I'll make sure to pay attention to the framework.
|
||||
|
||||
So, you want me to modify the `mathweb/flask/app.go` file in the Go Gin framework instead of Python Flask.
|
||||
|
||||
Please confirm if I should proceed with the changes. If so, please provide more details about what changes are needed.
|
||||
|
||||
---
|
||||
|
||||
- Datetime: 2025-02-16 02:38:40
|
||||
|
||||
- Model: alidsv3/deepseek-v3
|
||||
|
||||
- Selected files:
|
||||
- tree.txt
|
||||
- api/routes.go
|
||||
- config/config.go
|
||||
- middleware/auth.go
|
||||
- models/models.go
|
||||
- server/server.go
|
||||
- main.go
|
||||
|
||||
> 我在一个空目录下, 我需要创建一饿go语言gin框架的api后端项目,项目名字叫 zjmud.xyz/phyer/rbac, 需要帮我生成 go.mod的内容,
|
||||
> > 然后需要创建一个用户账号系统, 包括基础的用户验证功能和鉴权,鉴权依照rbac模式,管理员可以创建角色、用户、定义资源、资源组、和动作,管理员可以委派某个用户,对特定资源组和用户组进行动作定义, 请把go文件根据逻辑,放置到不同的模块中,不要都放到main 模块下
|
||||
>
|
||||
> 那么当前目录结构是否在正确的方向上, 每个文件是否有需要改动的地方
|
||||
|
||||
根据你的需求,当前目录结构已经在一个很好的方向上,但还需要做一些调整和补充。让我们一步一步来:
|
||||
|
||||
1. 首先,创建 `go.mod` 文件:
|
||||
|
||||
Filepath: go.mod
|
||||
Replace lines: 1-0
|
||||
```unknown
|
||||
module 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/sqlite v1.5.4
|
||||
gorm.io/gorm v1.25.5
|
||||
github.com/caarlos0/env/v10 v10.0.0
|
||||
)
|
||||
```
|
||||
|
||||
2. 当前目录结构基本合理,但建议做一些调整:
|
||||
|
||||
```
|
||||
.
|
||||
├── api
|
||||
│ └── routes.go
|
||||
├── config
|
||||
│ └── config.go
|
||||
├── controllers
|
||||
│ ├── auth.go
|
||||
│ ├── rbac.go
|
||||
│ └── user.go
|
||||
├── go.mod
|
||||
├── go.sum
|
||||
├── main.go
|
||||
├── middleware
|
||||
│ └── auth.go
|
||||
├── models
|
||||
│ └── models.go
|
||||
├── repositories
|
||||
│ ├── permission.go
|
||||
│ ├── role.go
|
||||
│ ├── user.go
|
||||
│ └── user_group.go
|
||||
├── server
|
||||
│ └── server.go
|
||||
└── services
|
||||
├── auth.go
|
||||
├── rbac.go
|
||||
└── user.go
|
||||
```
|
||||
|
||||
3. 需要修改和补充的文件:
|
||||
|
||||
Filepath: api/routes.go
|
||||
Replace lines: 9-26
|
||||
```go
|
||||
func SetupRoutes(r *gin.Engine) {
|
||||
auth := r.Group("/auth")
|
||||
{
|
||||
auth.POST("/login", controllers.Login)
|
||||
auth.POST("/register", controllers.Register)
|
||||
}
|
||||
|
||||
admin := r.Group("/admin")
|
||||
admin.Use(middleware.RBACMiddleware("admin", "full-access"))
|
||||
{
|
||||
admin.POST("/roles", controllers.CreateRole)
|
||||
admin.POST("/users", controllers.CreateUser)
|
||||
admin.POST("/permissions", controllers.CreatePermission)
|
||||
admin.POST("/user-groups", controllers.CreateUserGroup)
|
||||
admin.POST("/assign-role", controllers.AssignRoleToUser)
|
||||
admin.POST("/assign-permission", controllers.AssignPermissionToRole)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Filepath: config/config.go
|
||||
Replace lines: 15-19
|
||||
```go
|
||||
func LoadConfig() *Config {
|
||||
cfg := &Config{}
|
||||
if err := env.Parse(cfg); err != nil {
|
||||
panic("Failed to load config: " + err.Error())
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
```
|
||||
|
||||
Filepath: middleware/auth.go
|
||||
Replace lines: 8-13
|
||||
```go
|
||||
func JWTAuthMiddleware(secret string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
tokenString := c.GetHeader("Authorization")
|
||||
if tokenString == "" {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Authorization header missing"})
|
||||
return
|
||||
}
|
||||
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
return []byte(secret), nil
|
||||
})
|
||||
|
||||
if err != nil || !token.Valid {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
|
||||
return
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token claims"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("userID", claims["sub"])
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
4. 需要创建的新文件:
|
||||
|
||||
Filepath: controllers/auth.go
|
||||
<SEARCH>
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
"zjmud.xyz/phyer/rbac/models"
|
||||
"zjmud.xyz/phyer/rbac/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
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(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := services.Authenticate(loginData.Username, loginData.Password)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})
|
||||
return
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"sub": user.ID,
|
||||
"exp": time.Now().Add(time.Hour * 24).Unix(),
|
||||
})
|
||||
|
||||
tokenString, err := token.SignedString([]byte("your-256-bit-secret"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"token": tokenString})
|
||||
}
|
||||
|
||||
func Register(c *gin.Context) {
|
||||
var user models.User
|
||||
if err := c.ShouldBindJSON(&user); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := services.CreateUser(&user); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, user)
|
||||
}
|
||||
```
|
||||
|
||||
Filepath: services/auth.go
|
||||
<SEARCH>
|
||||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"zjmud.xyz/phyer/rbac/models"
|
||||
"zjmud.xyz/phyer/rbac/repositories"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func Authenticate(username, password string) (*models.User, error) {
|
||||
user, err := repositories.GetUserByUsername(username)
|
||||
if err != nil {
|
||||
return nil, errors.New("user not found")
|
||||
}
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
|
||||
return nil, errors.New("invalid password")
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func CreateUser(user *models.User) error {
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user.Password = string(hashedPassword)
|
||||
return repositories.CreateUser(user)
|
||||
}
|
||||
```
|
||||
|
||||
Filepath: repositories/user.go
|
||||
|
||||
|
34
tree.txt
34
tree.txt
@ -1,17 +1,37 @@
|
||||
.
|
||||
├── api
|
||||
│ └── routes.go
|
||||
│ └── routes.go
|
||||
├── config
|
||||
│ └── config.go
|
||||
│ └── config.go
|
||||
├── controllers
|
||||
│ ├── auth.go
|
||||
│ ├── rbac.go
|
||||
│ └── user.go
|
||||
├── go.mod
|
||||
├── go.sum
|
||||
├── main.go
|
||||
├── middleware
|
||||
│ └── auth.go
|
||||
│ ├── auth.go
|
||||
│ └── rbac.go
|
||||
├── models
|
||||
│ └── models.go
|
||||
│ ├── permission.go
|
||||
│ ├── role.go
|
||||
│ ├── user.go
|
||||
│ └── user_group.go
|
||||
├── repositories
|
||||
│ ├── permission.go
|
||||
│ ├── role.go
|
||||
│ ├── user.go
|
||||
│ └── user_group.go
|
||||
├── server
|
||||
│ └── server.go
|
||||
│ └── server.go
|
||||
├── services
|
||||
│ ├── auth.go
|
||||
│ ├── rbac.go
|
||||
│ └── user.go
|
||||
├── utils
|
||||
│ ├── jwt.go
|
||||
│ ├── redis.go
|
||||
│ └── response.go
|
||||
├── sug.md
|
||||
└── tree.txt
|
||||
|
||||
6 directories, 9 files
|
||||
|
29
utils/jwt.go
Normal file
29
utils/jwt.go
Normal file
@ -0,0 +1,29 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"gitea.zjmud.xyz/phyer/rbac/config"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"time"
|
||||
)
|
||||
|
||||
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")
|
||||
}
|
0
utils/redis.go
Normal file
0
utils/redis.go
Normal file
0
utils/response.go
Normal file
0
utils/response.go
Normal file
Loading…
x
Reference in New Issue
Block a user