You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Gin框架集成Clerk实现预约接口鉴权改造方案咨询

Gin + Clerk 身份认证适配方案与预约CRUD示例

一、Clerk Gin中间件实现

Clerk官方Go SDK提供的clerk.WithAuth()是针对标准库http.Handler的,我们需要将其封装为Gin兼容的中间件:

package middleware

import (
	"net/http"

	"github.com/clerk/clerk-sdk-go/v2"
	"github.com/gin-gonic/gin"
)

// ClerkAuth 适配Gin的Clerk身份认证中间件
func ClerkAuth() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 用Clerk鉴权逻辑包装Gin请求
		clerkHandler := clerk.WithAuth(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// 鉴权通过后,将用户信息存入Gin上下文
			activeUser, err := clerk.GetActiveUser(r.Context())
			if err != nil {
				c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "无效或缺失认证令牌"})
				return
			}
			c.Set("clerk_user", activeUser)
			c.Next()
		}))

		// 执行Clerk鉴权
		clerkHandler.ServeHTTP(c.Writer, c.Request)

		// 若鉴权失败已终止请求,直接返回
		if c.IsAborted() {
			return
		}
	}
}

二、完整Gin应用示例

以下是集成Clerk中间件的预约CRUD接口代码,假设你已完成PostgreSQL连接及业务层CRUD逻辑(用appointmentService指代你的业务实现):

package main

import (
	"log"
	"os"

	"github.com/clerk/clerk-sdk-go/v2"
	"github.com/gin-gonic/gin"
	"your-project-path/middleware"
	"your-project-path/services"
)

func main() {
	// 初始化Clerk客户端
	clerk.SetKey(os.Getenv("CLERK_SECRET_KEY"))

	// 初始化Gin引擎
	r := gin.Default()

	// 注册全局基础中间件
	r.Use(gin.Logger())
	r.Use(gin.Recovery())

	// 定义需鉴权的API分组
	api := r.Group("/api")
	api.Use(middleware.ClerkAuth()) // 给整个API分组添加鉴权保护

	// 预约管理CRUD路由
	appointments := api.Group("/appointments")
	{
		appointments.GET("", getAppointments)
		appointments.POST("", createAppointment)
		appointments.PUT("/:id", updateAppointment)
		appointments.DELETE("/:id", deleteAppointment)
	}

	// 启动服务
	if err := r.Run(":8080"); err != nil {
		log.Fatalf("服务启动失败: %v", err)
	}
}

// 获取当前用户的预约列表
func getAppointments(c *gin.Context) {
	// 从上下文取出Clerk用户信息
	user, ok := c.Get("clerk_user")
	if !ok {
		c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "获取用户信息失败"})
		return
	}
	clerkUser := user.(*clerk.User)

	// 调用业务层获取用户专属预约
	appointments, err := services.AppointmentService.GetByUserID(clerkUser.ID)
	if err != nil {
		c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, appointments)
}

// 创建新预约
func createAppointment(c *gin.Context) {
	var req struct {
		CustomerID string `json:"customer_id" binding:"required"`
		StartTime  string `json:"start_time" binding:"required"`
		ServiceID  string `json:"service_id" binding:"required"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// 获取当前登录用户ID
	user := c.MustGet("clerk_user").(*clerk.User)

	// 调用业务层创建预约
	appointment, err := services.AppointmentService.Create(user.ID, req.CustomerID, req.StartTime, req.ServiceID)
	if err != nil {
		c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, appointment)
}

// 更新指定预约
func updateAppointment(c *gin.Context) {
	appointmentID := c.Param("id")
	var req struct {
		Status    string `json:"status"`
		StartTime string `json:"start_time"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	user := c.MustGet("clerk_user").(*clerk.User)

	// 调用业务层更新预约(需校验预约归属当前用户)
	updated, err := services.AppointmentService.Update(user.ID, appointmentID, req)
	if err != nil {
		c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, updated)
}

// 删除指定预约
func deleteAppointment(c *gin.Context) {
	appointmentID := c.Param("id")
	user := c.MustGet("clerk_user").(*clerk.User)

	// 调用业务层删除预约(需校验用户权限)
	if err := services.AppointmentService.Delete(user.ID, appointmentID); err != nil {
		c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.Status(http.StatusNoContent)
}

三、改造指导

  1. 中间件迁移:

    • 替换原ServeMux中clerk.WithAuth()的使用方式,改用上述封装的Gin中间件
    • 可选择给整个API分组添加中间件,或给单个路由单独添加(如appointments.GET("", middleware.ClerkAuth(), getAppointments))
  2. 用户信息传递:

    • 在Gin handler中通过c.Get("clerk_user")或c.MustGet("clerk_user")获取认证用户,注意类型断言为*clerk.User
    • 业务逻辑中必须用用户ID做数据隔离,确保按摩师只能访问自己的预约数据
  3. 环境与配置:

    • 确保CLERK_SECRET_KEY环境变量配置正确(与原ServeMux项目一致)
  4. 权限校验强化:

    • 更新/删除预约时,业务层必须校验该预约属于当前登录的按摩师,防止越权操作
    • 可统一错误响应格式,与API整体风格对齐
  5. 路由结构优化:

    • 利用Gin的分组功能,将需鉴权的接口统一归类,避免重复添加中间件

内容的提问来源于stack exchange,提问作者Zoltán Orosz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 09:57:06