GoLand 项目从 0 到 1:第四天 —— 技术选型落地与方案设计

第四天核心任务:表设计、接口文档、权限体系搭建、认证落地

首先我们确定了技术选型:Golang + docker + 社区版配置中心 + min.io(文件存储)+ PgSQL(关系型数据库)+ Redis + Neo4j(记忆图谱)+docker-compse+ansible+shell+python

项目模块分配:我负责用户权限管理及文档列表

主要任务:表设计,接口文档设计,实现方案等

一、模块概述:用户权限管理的核心目标

用户权限管理模块需实现三大核心功能:

  • 用户 - 角色 - 权限的关联管理:通过多表关联实现灵活的权限分配
  • 接口级权限校验:每个接口请求需经过权限验证,确保合法访问
  • 身份认证与会话管理:基于 JWT 实现无状态的用户身份验证

同时需支撑文档列表模块的权限控制(如 "谁能查看 / 编辑哪些文档"),因此设计时需预留与业务模块的权限关联扩展点。

二、数据模型设计:五表联动的权限体系

为实现 "用户 - 角色 - 权限" 的多对多关系,设计五张核心表,形成完整的权限数据链路:

表名 作用 核心字段
tb_user 存储用户基础信息 id(用户 ID)、username(用户名)、password_hash(密码哈希)
tb_role 定义角色类型(如管理员、普通用户) id(角色 ID)、role_name(角色名称)、description(描述)
tb_user_role_ref 用户与角色的关联映射 iduser_id(关联用户)、role_id(关联角色)
tb_permission 定义权限点(如 "文档编辑""用户管理") idperm_key(权限标识,如doc:edit)、description
tb_role_permission 角色与权限的关联映射 idrole_id(关联角色)、perm_id(关联权限)

设计逻辑

  1. 一个用户对应一个角色(如 "管理员 + 编辑"),通过tb_user_role_ref关联
  2. 一个角色可包含多个权限(如 "管理员" 包含所有权限),通过tb_role_permission关联
  3. 权限点与接口一一对应(如接口/files/upload对应权限file:upload

三、权限控制流程:从请求到响应的校验链路

通过时序图梳理权限校验的完整流程:

四、JWT 认证实现:无状态的身份验证

基于 JWT(JSON Web Token)实现用户身份认证,核心代码如下:

登录时生成JWT令牌

Go 复制代码
// Claims JWT声明结构
type Claims struct {
	UserID   int64  `json:"user_id"`
	Username string `json:"username"`
	Role     string `json:"role"`
	jwt.RegisteredClaims
}

// GenerateToken 生成JWT令牌
func GenerateToken(userID int64, username, role string) (string, error) {
	cfg := config.GetConfig()

	claims := Claims{
		UserID:   userID,
		Username: username,
		Role:     role,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(cfg.JWT.ExpireTime) * time.Second)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			NotBefore: jwt.NewNumericDate(time.Now()),
		},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString([]byte(cfg.JWT.Secret))
}
func main() {
	token, _ := GenerateToken(10000, "zhudayang", "admin")
	fmt.Println(token)
}

// ParseToken 解析JWT令牌
func ParseToken(tokenString string) (*Claims, error) {
	cfg := config.GetConfig()

	token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(cfg.JWT.Secret), nil
	})

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(*Claims); ok && token.Valid {
		return claims, nil
	}

	return nil, errors.New("invalid token")
}

// RefreshToken 刷新令牌
func RefreshToken(tokenString string) (string, error) {
	claims, err := ParseToken(tokenString)
	if err != nil {
		return "", err
	}

	// 生成新令牌
	return GenerateToken(claims.UserID, claims.Username, claims.Role)
}

解析JWT令牌:

Go 复制代码
// Auth 认证中间件
func Auth() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 获取Authorization头
		authHeader := c.GetHeader("Authorization")
		if authHeader == "" {
			stackTrace := getStackTrace()
			logger.Error(fmt.Sprintf(
				" | Authorization缺失 - method: %s | path: %s | client_ip: %s | user_agent: %s\nStack trace:\n%s",
				c.Request.Method,
				c.Request.URL.Path,
				c.ClientIP(),
				c.Request.UserAgent(),
				stackTrace,
			))
			response.Unauthorized(c, "Authorization header is required")
			c.Abort()
			return
		}

		// 检查Bearer前缀
		parts := strings.Split(authHeader, " ")
		if len(parts) != 2 || parts[0] != "Bearer" {
			stackTrace := getStackTrace()
			logger.Error(fmt.Sprintf(
				" | 无效token - method: %s | path: %s | client_ip: %s | auth_header: %s\nStack trace:\n%s",
				c.Request.Method,
				c.Request.URL.Path,
				c.ClientIP(),
				authHeader,
				stackTrace,
			))
			response.Unauthorized(c, "Invalid authorization header format")
			c.Abort()
			return
		}

		token := parts[1]

		// 解析JWT令牌
		claims, err := auth.ParseToken(token)
		if err != nil {
			stackTrace := getStackTrace()
			logger.Error(fmt.Sprintf(
				" | 解析JWT令牌 - method: %s | path: %s | client_ip: %s | error: %s\nStack trace:\n%s",
				c.Request.Method,
				c.Request.URL.Path,
				c.ClientIP(),
				err.Error(),
				stackTrace,
			))
			response.Unauthorized(c, "token is expired")
			c.Abort()
			return
		}

		// 将用户信息存储到上下文中
		c.Set("user_id", claims.UserID)
		c.Set("username", claims.Username)
		c.Set("role", claims.Role)

		// 记录认证成功
		logger.Info(fmt.Sprintf(
			" | 认证成功 - method: %s | path: %s | client_ip: %s | user_id: %d | username: %s | role: %s",
			c.Request.Method,
			c.Request.URL.Path,
			c.ClientIP(),
			claims.UserID,
			claims.Username,
			claims.Role,
		))

		c.Next()
	}
}

六、总结与次日计划

第四天成果

  1. 完成用户权限管理模块的五表数据模型设计,明确 "用户 - 角色 - 权限" 的关联逻辑
  2. 梳理权限校验流程,通过时序图固化接口请求的权限判断链路
  3. 实现 JWT 令牌生成功能,为身份认证提供核心支撑
相关推荐
liulanba2 小时前
八股取士-go
golang
raoxiaoya4 小时前
Golang中的`io.Copy()`使用场景
开发语言·后端·golang
枫叶梨花10 小时前
使用Go语言获取Windows系统信息:从CPU到电池的全维度监控
开发语言·windows·golang
哈基咩10 小时前
Go 语言模糊测试 (Fuzz Testing) 深度解析与实践
开发语言·后端·golang
魔都吴所谓17 小时前
【go】map基础操作
开发语言·后端·golang
澡点睡觉1 天前
golang的包和闭包
开发语言·后端·golang
比特森林探险记1 天前
Go语言常用的设计模式
开发语言·设计模式·golang
静谧之心1 天前
Go 工程化全景:从目录结构到生命周期的完整服务框架
开发语言·golang·channel·工程化·goroutine