当前位置: 首页 > news >正文

GoLand 项目从 0 到 1:第五天 —— 角色权限中间件实现与事务控制

第五天核心任务:权限校验链路闭环

第五天的开发聚焦于权限控制的核心实现,完成了角色权限中间件的开发,实现了接口级别的权限校验,并基于事务控制确保用户权限操作的数据一致性。通过这部分工作,系统的权限管理从设计阶段正式进入可运行阶段,为后续业务模块的安全接入提供了坚实基础。

一、角色权限中间件

1. 中间件完整代码

func RequirePermission() gin.HandlerFunc {return func(c *gin.Context) {// 新建数据库连接实例db := postgresqldb.GetDB()// 1. 获取当前请求的路径和方法path := c.FullPath()fmt.Println("请求路径:", path)// 2. 查询该接口所需的权限IDvar interfacePerm postgresql.InterfacePermissionReferr := db.Where("name = ? AND is_delete = 0", path).First(&interfacePerm).Error// 接口未配置权限,直接通过(无需权限校验)if err != nil && err == gorm.ErrRecordNotFound {c.Next()return}if err != nil {response.InternalServerError(c, "查询接口权限配置失败")c.Abort()return}// 3. 接口配置了权限,需要验证用户权限// 从上下文获取用户ID(由JWT中间件提前解析存入)userID, exists := c.Get("user_id")if !exists {response.Unauthorized(c, "未获取到用户信息,请先登录")c.Abort()return}uid, ok := userID.(int64)if !ok {response.InternalServerError(c, "用户ID格式错误")c.Abort()return}// 4. 查询用户拥有的所有权限IDvar permissions []struct {PermissionID int64 `gorm:"column:permission_id"`}// 关联查询:用户->角色->权限err = db.Table("tb_user_role_ref AS ur").Joins("JOIN tb_role_permission AS rp ON ur.role_id = rp.role_id").Where("ur.user_id = ? AND ur.is_delete = 0 AND rp.is_delete = 0", uid).Select("rp.permission_id").Find(&permissions).Errorif err != nil {response.InternalServerError(c, "查询用户权限失败")c.Abort()return}// 5. 权限比对:检查用户是否拥有接口所需权限userPermSet := make(map[int64]struct{})for _, perm := range permissions {userPermSet[perm.PermissionID] = struct{}{} // 用map存储权限ID,提高查询效率}requiredPermID := interfacePerm.PermissionIDif _, hasPermission := userPermSet[requiredPermID]; !hasPermission {response.Forbidden(c, "没有访问该接口的权限")c.Abort()return}// 6. 验证通过,继续处理请求c.Next()}
}

2. 中间件集成方式

在路由注册时,为需要权限控制的接口绑定该中间件:

// 示例:为文件管理接口添加权限校验
files := authenticated.Group("/files")
files.Use(middleware.RequirePermission()) // 绑定权限中间件
{files.POST("/upload", fileHandler.UploadFile)files.GET("/list", fileHandler.ListFiles)
}

二、用户权限接口开发与事务控制

用户权限相关接口(如分配角色、修改权限)涉及多表操作,需通过事务确保数据一致性。以下是核心接口的实现示例:

1. 创建用户角色接口(带事务控制)

routes:

		userHandler := handlers.NewUserHandler()user := postgresqlAPI.Group("/user"){user.GET("/create", userHandler.CreateUser)user.POST("/update", userHandler.UpdateUser)user.POST("/delete", userHandler.DeleteUser)//.....其它权限接口,这里展示增删改}

handler

func (h *UserHandler) CreateUser(c *gin.Context) {var req postgresql.CreateUserRoleRefRequestif err := c.ShouldBindJSON(&req); err != nil {response.BadRequest(c, "无效的请求参数: "+err.Error())return}// 调用Service层处理业务逻辑(包含事务)result, err := h.service.CreateUser(&req)if err != nil {response.InternalServerError(c, "Failed to create user")return}response.Success(c, result)
}

model

type User struct {BaseModelAccount  string `json:"account" gorm:"type:varchar(100);not null;uniqueIndex"`Username string `json:"username" gorm:"type:varchar(50);not null;uniqueIndex"`Password string `json:"password" gorm:"type:varchar(255);not null"`
}// TableName 指定表名
func (User) TableName() string {return "tb_user"
}// CreateUserRoleRefRequest 新建用户请求参数
type CreateUserRoleRefRequest struct {UserName string `json:"userName" binding:"required" validate:"required,min=3,max=50"`Password string `json:"password" binding:"required" validate:"required,min=6"`RoleId   int64  `json:"roleId" binding:"required" validate:"required,gt=0"`
}// UpdateUserRequest 编辑用户请求参数
type UpdateUserRequest struct {UserId   int64  `json:"userId" binding:"required" validate:"required"`UserName string `json:"userName" binding:"required" validate:"required,min=3,max=50"`Password string `json:"password" binding:"required" validate:"required,min=6"`RoleId   int64  `json:"roleId" binding:"required" validate:"required,gt=0"`
}

service

// CreateUser 创建用户(包含角色关联,使用事务)
func (s *UserService) CreateUser(req *postgresql.CreateUserRoleRefRequest) (*postgresql.User, error) {// 获取数据库事务管理器,用于处理事务操作txManager := postgresqldb.GetTransactionManager()// 创建背景上下文,用于事务控制ctx := context.Background()// 执行带返回值的事务:所有数据库操作在同一事务中执行,确保原子性result, err := txManager.TransactionResult(ctx, func(tx *gorm.DB) (interface{}, error) {// 密码加密:使用工具函数对原始密码进行bcrypt加密处理hashPassword, err := utils.HashPassword(req.Password)if err != nil {return nil, fmt.Errorf("password encryption failed: %w", err)}// 生成唯一账号:格式为"hc_用户名_时间戳",确保账号唯一性timestamp := strconv.FormatInt(time.Now().Unix(), 10)  // 获取当前时间戳(秒级)account := "hc_" + req.UserName + "_" + timestamp       // 拼接账号字符串// 创建用户记录:初始化用户对象并保存到数据库user := &postgresql.User{Account:  account,    // 唯一账号Username: req.UserName, // 用户名(可重复)Password: hashPassword, // 加密后的密码}if err := tx.Create(user).Error; err != nil {return nil, fmt.Errorf("failed to create user: %w", err)}// 关联用户角色:建立用户与角色的多对多关系userRole := &postgresql.UserRoleRef{UserID: user.ID,  // 新创建用户的IDRoleID: req.RoleId, // 请求中指定的角色ID}if err := tx.Create(userRole).Error; err != nil {return nil, fmt.Errorf("failed to create user role relation: %w", err)}// 验证角色有效性:检查指定角色是否存在且未被删除(is_delete=0)var role postgresql.Roleif err := tx.Where("id = ? AND is_delete = 0", req.RoleId).First(&role).Error; err != nil {return nil, fmt.Errorf("failed to get role: %w", err)}// 组装返回数据:仅返回必要的用户信息(基础字段+账号信息)return &postgresql.User{BaseModel: postgresql.BaseModel{ID:         user.ID,          // 用户IDCreateTime: user.CreateTime,  // 创建时间UpdateTime: user.UpdateTime,  // 更新时间(初始与创建时间一致)},Username: user.Username, // 用户名Account:  user.Account,  // 唯一账号Password: user.Password, // 加密后的密码(注意:生产环境可能需移除密码返回)}, nil})// 事务执行失败:返回错误信息if err != nil {return nil, err}// 事务执行成功:将结果转换为User类型并返回return result.(*postgresql.User), nil
}
// TransactionResult 执行带返回值的数据库事务
// 功能:在一个原子事务中执行数据库操作,并返回操作结果
// 参数:
//   ctx - 上下文对象,用于控制事务超时和取消
//   fn - 事务处理函数,接收 *gorm.DB 事务对象,返回操作结果和错误
// 返回值:
//   interface{} - 事务执行成功时返回的结果
//   error - 事务执行失败时返回的错误(包括数据库未初始化、事务开始失败、业务逻辑错误、提交失败等)
func (tm *TransactionManager) TransactionResult(ctx context.Context, fn TxResultFunc) (interface{}, error) {// 检查数据库连接是否已初始化if tm.db == nil {return nil, fmt.Errorf("database is not initialized")}// 开始事务:使用上下文创建新事务,确保事务操作受上下文控制tx := tm.db.WithContext(ctx).Begin()if tx.Error != nil {return nil, tx.Error // 事务开始失败(如数据库连接问题)}// 延迟处理:确保在函数退出前处理事务回滚(针对panic场景)defer func() {if r := recover(); r != nil {tx.Rollback() // 发生panic时回滚事务panic(r)      // 重新抛出panic,让上层处理}}()// 执行事务逻辑:调用业务定义的事务处理函数result, err := fn(tx)if err != nil {tx.Rollback() // 业务逻辑执行失败,回滚事务return nil, err}// 提交事务:所有业务逻辑执行成功后提交事务if err := tx.Commit().Error; err != nil {return nil, err // 提交失败(如数据库异常)}// 返回事务执行结果return result, nil
}

2. 事务控制的核心作用

在用户权限操作中,事务确保了多表操作的原子性:

  • 删除旧关联 + 插入新关联:两个操作必须同时成功或同时失败,避免出现 “部分角色分配” 的中间状态
  • 异常处理:通过tx.Rollback()在任何步骤失败时回滚所有操作,保证数据一致性
  • 性能优化:批量插入(Create(&roleRefs))减少数据库交互次数,配合事务提升效率

三、总结与次日计划

第五天成果

  1. 完成RequirePermission中间件,实现接口级权限校验,支持 “接口 - 权限 - 用户” 的联动验证
  2. 开发用户权限核心接口,通过 GORM 事务确保多表操作的数据一致性
  3. 形成完整的权限控制链路:JWT 认证→权限中间件校验→业务接口执行
http://www.dtcms.com/a/316029.html

相关文章:

  • Java学习第一百零六部分——Lucene
  • python开发环境安装多系统完整版
  • laravel在cli模式下输出格式漂亮一些
  • python的高校班级管理系统
  • 【计组】数据的表示与运算
  • Linux动静态库的理解
  • 探秘MOBILITY China 2026,新能源汽车与智慧出行的未来盛宴
  • MinIO02-Docker安装
  • 11.Linux 防火墙管理
  • selenium自动化收集资料
  • MLP-Mixer: An all-MLP Architecture for Vision
  • 计算机基础:操作系统学习的基石
  • 【前端】Node.js 简易服务器搭建全指南:从基础到实践
  • 调试|谷歌浏览器调试长连接|调试SSE和websocket
  • Redis内存耗尽时的应对策略
  • Day115 SpringBoot整合Redis,RedisTemplate和注解两种方式的使用
  • SQL164 删除表
  • 输入12-21V输出5V 10A电源转换芯片方案
  • 什么是模型并行?
  • C语言基础_随机数、数组、函数、指针
  • 注意点:如何使用conda创建虚拟环境并使用虚拟环境以及当安装相关库时,如何指定安装到那个环境里面 ---待看
  • Java中公用字符串工具类拿走、直接用
  • 山东省天地图API申请并加载到QGIS和ArcGIS Pro中
  • 什么是mysql的垂直分表,理论依据是什么,如何使用?
  • 无人机光伏识别误检率↓83%!陌讯多模态融合算法实战解析
  • PHP获取淘宝商品详情返回参数详解
  • K8S 性能瓶颈排查
  • 路由的类型
  • Linux驱动学习(八)设备树
  • 无人机 × 巡检 × AI识别:一套可复制的超低延迟低空视频感知系统搭建实践