当前位置: 首页 > 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.lryc.cn/news/611296.html

相关文章:

  • 前端工程化:Vue3(二)
  • 贝叶斯统计从理论到实践
  • 自动牙龈边缘识别软件设计与实现
  • Android AppSearch 深度解析:现代应用搜索架构与实践
  • 消息队列疑难问题(RocketMQ)
  • 认识爬虫 —— bs4提取
  • 阿里招AI产品运营
  • 永磁同步电机的矢量控制
  • RK3568下使用Qt 绘制实现实时坐标曲线
  • 【Spring Cloud】-- 注册中心
  • PowerShell 入门2: 使用帮助系统
  • 异或游戏 运算符优先级问题
  • GB28181监控平台LiveGBS如何配置GB28181对接海康、大华解码器上墙,将GB28181平台是视频给硬件解码器解码上墙
  • cJSON库应用
  • C语言的常见错误与调试
  • uniapp renderjs 逻辑层,视图层互相传递数据封装
  • 背包初步练习
  • 计算机视觉面试保温:CLIP(对比语言-图像预训练)和BERT技术概述
  • Linux逻辑卷管理操作指南
  • 论文解读:Mamba: Linear-Time Sequence Modeling with Selective State Spaces
  • JSP相关Bug解决
  • AutoSar AP LT规范中 建模消息和非建模消息都可以使用LogInfo() API吗?
  • 达芬奇31-40
  • stm32F407 硬件COM事件触发六步换相
  • AI赋能复合材料与智能增材制造:前沿技术研修重磅
  • 智能融合:增材制造多物理场AI建模与工业应用实战
  • 【面向对象】面向对象七大原则
  • linux nfs+autofs
  • 注意点:Git 从安装到分支协作、冲突解决的完整步骤 ---待修改,没看这个步骤,需要重新整理步骤
  • ara::log::LogStream::WithTag的概念和使用案例