This commit is contained in:
zhangkun9038@dingtalk.com 2025-02-16 20:28:47 +08:00
parent ae75582d11
commit 3cc2b18081
16 changed files with 413 additions and 128 deletions

View File

@ -5,12 +5,20 @@ import (
"strconv" "strconv"
) )
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 { type Config struct {
DBHost string DB DBConfig
DBPort int
DBUser string
DBPassword string
DBName string
JWTSecret string JWTSecret string
RedisHost string RedisHost string
RedisPort int RedisPort int
@ -21,11 +29,13 @@ var AppConfig Config
func Init() { func Init() {
AppConfig = Config{ AppConfig = Config{
DBHost: getEnv("DB_HOST", "localhost"), DB: DBConfig{
DBPort: getEnvAsInt("DB_PORT", 3306), Host: getEnv("DB_HOST", "localhost"),
DBUser: getEnv("DB_USER", "root"), Port: getEnvAsInt("DB_PORT", 3306),
DBPassword: getEnv("DB_PASSWORD", ""), User: getEnv("DB_USER", "root"),
DBName: getEnv("DB_NAME", "rbac"), Password: getEnv("DB_PASSWORD", ""),
Name: getEnv("DB_NAME", "rbac"),
},
JWTSecret: getEnv("JWT_SECRET", "secret"), JWTSecret: getEnv("JWT_SECRET", "secret"),
RedisHost: getEnv("REDIS_HOST", "localhost"), RedisHost: getEnv("REDIS_HOST", "localhost"),
RedisPort: getEnvAsInt("REDIS_PORT", 6379), RedisPort: getEnvAsInt("REDIS_PORT", 6379),

View File

@ -3,6 +3,7 @@ package controllers
import ( import (
"gitea.zjmud.xyz/phyer/rbac/services" "gitea.zjmud.xyz/phyer/rbac/services"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"strconv"
) )
// CreateRole 创建新角色 // CreateRole 创建新角色
@ -93,6 +94,63 @@ func AssignPermissionToRole(c *gin.Context) {
c.JSON(200, gin.H{"message": "Permission assigned successfully"}) 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 获取所有权限 // GetPermissions 获取所有权限
func GetPermissions(c *gin.Context) { func GetPermissions(c *gin.Context) {
permissions, err := services.GetAllPermissions() permissions, err := services.GetAllPermissions()
@ -102,3 +160,60 @@ func GetPermissions(c *gin.Context) {
} }
c.JSON(200, permissions) 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"})
}

18
main.go
View File

@ -1,3 +1,4 @@
<<<<<<< HEAD
package main package main
import ( import (
@ -13,6 +14,23 @@ func main() {
api.SetupRoutes(r) api.SetupRoutes(r)
r.Run(":8080") r.Run(":8080")
} }
=======
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")
}
>>>>>>> Snippet
// package main // package main
// //

View File

@ -5,6 +5,11 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
// JWTAuth is an alias for AuthMiddleware
func JWTAuth() gin.HandlerFunc {
return AuthMiddleware()
}
func AuthMiddleware() gin.HandlerFunc { func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
token := c.GetHeader("Authorization") token := c.GetHeader("Authorization")

View File

@ -1,11 +1,12 @@
package models package models
import ( import (
"gorm.io/gorm"
"time" "time"
) )
type User struct { type User struct {
ID string `json:"id" gorm:"primaryKey"` gorm.Model
Username string `json:"username" gorm:"uniqueIndex;not null"` Username string `json:"username" gorm:"uniqueIndex;not null"`
Password string `json:"-" gorm:"not null"` Password string `json:"-" gorm:"not null"`
Email string `json:"email" gorm:"uniqueIndex;not null"` Email string `json:"email" gorm:"uniqueIndex;not null"`

BIN
rbac Executable file

Binary file not shown.

View File

@ -2,15 +2,17 @@ package repositories
import ( import (
"gitea.zjmud.xyz/phyer/rbac/config" "gitea.zjmud.xyz/phyer/rbac/config"
"gitea.zjmud.xyz/phyer/rbac/models"
"gorm.io/driver/mysql" "gorm.io/driver/mysql"
"gorm.io/gorm" "gorm.io/gorm"
"strconv"
) )
var db *gorm.DB var db *gorm.DB
func InitDB() error { func InitDB() error {
var err 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" dsn := config.AppConfig.DB.User + ":" + config.AppConfig.DB.Password + "@tcp(" + config.AppConfig.DB.Host + ":" + strconv.Itoa(config.AppConfig.DB.Port) + ")/" + config.AppConfig.DB.Name + "?charset=utf8mb4&parseTime=True&loc=Local"
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil { if err != nil {
return err return err

View File

@ -1,87 +1,55 @@
package repositories package repositories
import ( import (
"gitea.zjmud.xyz/phyer/rbac/models" "gitea.zjmud.xyz/phyer/rbac/models"
"gorm.io/gorm" "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) { func GetPermissionByID(id uint) (*models.Permission, error) {
var permission models.Permission var permission models.Permission
result := db.First(&permission, id) result := db.First(&permission, id)
return &permission, result.Error return &permission, result.Error
} }
func GetPermissions() ([]models.Permission, error) { func GetPermissions() ([]models.Permission, error) {
var permissions []models.Permission var permissions []models.Permission
result := db.Find(&permissions) result := db.Find(&permissions)
return permissions, result.Error return permissions, result.Error
} }
func UpdatePermission(id uint, updateData map[string]interface{}) (*models.Permission, error) { func UpdatePermission(id uint, updateData map[string]interface{}) (*models.Permission, error) {
var permission models.Permission var permission models.Permission
result := db.Model(&permission).Where("id = ?", id).Updates(updateData) result := db.Model(&permission).Where("id = ?", id).Updates(updateData)
if result.Error != nil { if result.Error != nil {
return nil, result.Error return nil, result.Error
} }
return GetPermissionByID(id) 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
}
package repositories
import (
"gitea.zjmud.xyz/phyer/rbac/models"
"gorm.io/gorm"
)
func CreatePermission(permission *models.Permission) (*models.Permission, error) { func CreatePermission(permission *models.Permission) (*models.Permission, error) {
result := db.Create(permission) result := db.Create(permission)
return permission, result.Error 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 { func DeletePermission(id uint) error {
result := db.Delete(&models.Permission{}, id) result := db.Delete(&models.Permission{}, id)
return result.Error return result.Error
} }
func CheckPermissionExists(name string) (bool, error) { func CheckPermissionExists(name string) (bool, error) {
var count int64 var count int64
result := db.Model(&models.Permission{}).Where("name = ?", name).Count(&count) result := db.Model(&models.Permission{}).Where("name = ?", name).Count(&count)
return count > 0, result.Error return count > 0, result.Error
} }
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 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)
}

View File

@ -10,12 +10,30 @@ func CreateRole(role *models.Role) (*models.Role, error) {
return role, result.Error return role, result.Error
} }
func GetAllRoles() ([]models.Role, error) {
var roles []models.Role
result := db.Find(&roles)
return roles, result.Error
}
func GetRoleByID(id uint) (*models.Role, error) { func GetRoleByID(id uint) (*models.Role, error) {
var role models.Role var role models.Role
result := db.Preload("Permissions").First(&role, id) result := db.First(&role, id)
return &role, result.Error 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) { func GetRoles() ([]models.Role, error) {
var roles []models.Role var roles []models.Role
result := db.Preload("Permissions").Find(&roles) result := db.Preload("Permissions").Find(&roles)
@ -36,16 +54,6 @@ func DeleteRole(id uint) error {
return result.Error 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 { 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}}) return db.Model(&models.Role{Model: gorm.Model{ID: roleID}}).Association("Permissions").Delete(&models.Permission{Model: gorm.Model{ID: permissionID}})
} }

View File

@ -1,20 +1,25 @@
package repositories package repositories
import ( import (
"gitea.zjmud.xyz/phyer/rbac/config"
"gitea.zjmud.xyz/phyer/rbac/models" "gitea.zjmud.xyz/phyer/rbac/models"
"gorm.io/gorm" // "gorm.io/gorm"
) )
var db *gorm.DB func CheckUserPermission(userID string, permission string) (bool, error) {
var user models.User
func InitDB() error { result := db.Preload("Roles.Permissions").Where("id = ?", userID).First(&user)
var err error if result.Error != nil {
db, err = gorm.Open(mysql.Open(config.GetDSN()), &gorm.Config{}) return false, result.Error
if err != nil {
return err
} }
return db.AutoMigrate(&models.User{})
for _, role := range user.Roles {
for _, perm := range role.Permissions {
if perm.Name == permission {
return true, nil
}
}
}
return false, nil
} }
func GetAllUsers() ([]models.User, error) { func GetAllUsers() ([]models.User, error) {

View File

@ -2,9 +2,30 @@ package server
import ( import (
"gitea.zjmud.xyz/phyer/rbac/api" "gitea.zjmud.xyz/phyer/rbac/api"
"gitea.zjmud.xyz/phyer/rbac/config"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gorm.io/driver/mysql"
"gorm.io/gorm"
) )
var db *gorm.DB
// InitDB initializes the database connection
func InitDB() {
var err error
dsn := config.AppConfig.DB.DSN()
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
}
// NewServer creates and returns a new Gin server
func NewServer() *gin.Engine {
r := gin.Default()
return r
}
func Start() { func Start() {
r := gin.Default() r := gin.Default()

View File

@ -2,10 +2,11 @@ package services
import ( import (
"errors" "errors"
"gitea.zjmud.xyz/phyer/rbac/models" // "gitea.zjmud.xyz/phyer/rbac/models"
"gitea.zjmud.xyz/phyer/rbac/repositories" "gitea.zjmud.xyz/phyer/rbac/repositories"
"gitea.zjmud.xyz/phyer/rbac/utils" "gitea.zjmud.xyz/phyer/rbac/utils"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"strconv"
) )
func Login(username, password string) (string, error) { func Login(username, password string) (string, error) {
@ -17,7 +18,7 @@ func Login(username, password string) (string, error) {
if err != nil { if err != nil {
return "", errors.New("invalid credentials") return "", errors.New("invalid credentials")
} }
token, err := utils.GenerateJWT(user.ID) token, err := utils.GenerateJWT(strconv.FormatUint(uint64(user.ID), 10))
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -4,12 +4,13 @@ import (
"errors" "errors"
"gitea.zjmud.xyz/phyer/rbac/models" "gitea.zjmud.xyz/phyer/rbac/models"
"gitea.zjmud.xyz/phyer/rbac/repositories" "gitea.zjmud.xyz/phyer/rbac/repositories"
"strconv"
) )
// AssignRoleToUser 为用户分配角色 // AssignRoleToUser 为用户分配角色
func AssignRoleToUser(userID string, roleID uint) error { func AssignRoleToUser(userID uint, roleID uint) error {
// 检查用户是否存在 // 检查用户是否存在
user, err := repositories.GetUserByID(userID) user, err := repositories.GetUserByID(strconv.FormatUint(uint64(userID), 10))
if err != nil { if err != nil {
return errors.New("user not found") return errors.New("user not found")
} }
@ -25,9 +26,9 @@ func AssignRoleToUser(userID string, roleID uint) error {
} }
// RemoveRoleFromUser 移除用户的角色 // RemoveRoleFromUser 移除用户的角色
func RemoveRoleFromUser(userID string, roleID uint) error { func RemoveRoleFromUser(userID uint, roleID uint) error {
// 检查用户是否存在 // 检查用户是否存在
user, err := repositories.GetUserByID(userID) user, err := repositories.GetUserByID(strconv.FormatUint(uint64(userID), 10))
if err != nil { if err != nil {
return errors.New("user not found") return errors.New("user not found")
} }
@ -71,6 +72,131 @@ func CreateRoleWithPermissions(roleName string, permissionIDs []uint) (*models.R
return role, nil 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 检查用户是否具有特定权限 // CheckUserPermission 检查用户是否具有特定权限
func CheckUserPermission(userID string, permission string) (bool, error) { func CheckUserPermission(userID string, permission string) (bool, error) {
// 获取用户的所有角色 // 获取用户的所有角色

View File

@ -3,7 +3,7 @@ package services
import ( import (
"gitea.zjmud.xyz/phyer/rbac/models" "gitea.zjmud.xyz/phyer/rbac/models"
"gitea.zjmud.xyz/phyer/rbac/repositories" "gitea.zjmud.xyz/phyer/rbac/repositories"
"gitea.zjmud.xyz/phyer/rbac/utils" // "gitea.zjmud.xyz/phyer/rbac/utils"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )

View File

@ -1,37 +1,41 @@
. .
├── api ├── api
└── routes.go    └── routes.go
├── config ├── config
└── config.go    └── config.go
├── controllers ├── controllers
├── auth.go    ├── auth.go
├── rbac.go    ├── rbac.go
└── user.go    └── user.go
├── go.mod ├── go.mod
├── go.sum ├── go.sum
├── main.go ├── main.go
├── middleware ├── middleware
├── auth.go    ├── auth.go
└── rbac.go    └── rbac.go
├── models ├── models
│ ├── permission.go │   ├── permission.go
│ ├── role.go │   ├── role.go
│ ├── user.go │   ├── user.go
│ └── user_group.go │   └── user_group.go
├── rbac
├── repositories ├── repositories
│ ├── permission.go │   ├── db.go
│ ├── role.go │   ├── permission.go
│ ├── user.go │   ├── role.go
│ └── user_group.go │   ├── user.go
│   └── user_group.go
├── server ├── server
└── server.go    └── server.go
├── services ├── services
│ ├── auth.go │   ├── auth.go
│ ├── rbac.go │   ├── rbac.go
│ └── user.go │   └── user.go
├── utils
│ ├── jwt.go
│ ├── redis.go
│ └── response.go
├── sug.md ├── sug.md
└── tree.txt ├── tree.txt
└── utils
├── jwt.go
├── redis.go
└── response.go
10 directories, 29 files

View File

@ -1,6 +1,7 @@
package utils package utils
import ( import (
"errors"
"gitea.zjmud.xyz/phyer/rbac/config" "gitea.zjmud.xyz/phyer/rbac/config"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
"time" "time"