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

Coze源码分析-API授权-删除令牌-后端源码

前言

本文将深入分析Coze Studio项目中API授权令牌删除功能的后端实现,通过源码解读来理解整个Personal Access Token (PAT) 删除功能的架构设计和技术实现。API授权令牌删除功能作为用户权限管理系统的重要组成部分,主要负责安全地删除用户创建的访问令牌,确保用户能够有效管理自己的API访问权限。

项目架构概览

整体架构设计

Coze Studio后端采用了经典的分层架构模式,将API授权令牌删除功能划分为以下几个核心层次:

┌─────────────────────────────────────────────────────────────┐
│                    IDL接口定义层                             │
│  ┌─────────────┐  ┌───────────────── ┐    ┌─────────────┐    │
│  │ base.thrift │  │openapiauth.thrift│    │ api.thrift  │    │
│  └─────────────┘  └───────────────── ┘    └─────────────┘    │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                    API网关层                                 │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐          │
│  │   Model     │  │   Handler   │  │   Router    │          │
│  │   定义      │  │   处理器     │  │   路由       │          │
│  └─────────────┘  └─────────────┘  └─────────────┘          │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                   应用服务层                                 │
│  ┌─────────────────────────────────────────────────────┐    │
│  │            OpenAuthApplicationService               │    │
│  │       DeletePersonalAccessTokenAndPermission        │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                   领域服务层                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              APIAuth Domain                         │   │
│  │                  Delete                             │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                   数据访问层                                 │
│         ┌─────────────┐          ┌─────────────┐            │
│         │ ApiKeyDAO   │          │ query&Model │            │
│         │             │          │  查询和数据  │            │
│         └─────────────┘          └─────────────┘            │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                   存储服务层                                 │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                MySQL数据库                           │    │
│  │                                                     │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘

1. IDL接口定义层

IDL基础类型定义(base.thrift)

文件位置:idl/base.thrift
核心代码:

namespace py base
namespace go base
namespace java com.bytedance.thrift.basestruct TrafficEnv {1: bool   Open = false,2: string Env  = ""   ,
}struct Base {1:          string             LogID      = "",2:          string             Caller     = "",3:          string             Addr       = "",4:          string             Client     = "",5: optional TrafficEnv         TrafficEnv     ,6: optional map<string,string> Extra          ,
}struct BaseResp {1:          string             StatusMessage = "",2:          i32                StatusCode    = 0 ,3: optional map<string,string> Extra             ,
}struct EmptyReq {
}struct EmptyData {}struct EmptyResp {1: i64       code,2: string    msg ,3: EmptyData data,
}struct EmptyRpcReq {255: optional Base Base,
}struct EmptyRpcResp {255: optional BaseResp BaseResp,
}

文件作用:
定义了项目中所有接口的基础数据结构,作为其他IDL文件的依赖基础。

IDL权限授权接口定义(openapiauth.thrift)

文件位置:idl/permission/openapiauth.thrift
核心代码:

struct DeletePersonalAccessTokenAndPermissionRequest {1: required i64 id  (api.js_conv="true")// PAT Id
}struct DeletePersonalAccessTokenAndPermissionResponse {1: required i32 code2: required string msg
}

IDL服务定义(openapiauth_service.thrift)

文件位置:idl/permission/openapiauth_service.thrift
核心代码:

service OpenAPIAuthService {openapiauth.DeletePersonalAccessTokenAndPermissionResponse DeletePersonalAccessTokenAndPermission (1: openapiauth.DeletePersonalAccessTokenAndPermissionRequest req) (api.post="/api/permission_api/pat/delete_personal_access_token_and_permission")
}

IDL主API服务聚合文件(api.thrift)

文件位置:idl/api.thrift
核心代码:

include "./permission/openapiauth_service.thrift"namespace go cozeservice OpenAPIAuthService extends openapiauth_service.OpenAPIAuthService {}
// 聚合多个业务服务接口
// 其他服务接口也会在此文件中聚合

文件作用:
项目的API聚合文件,统一组织所有业务服务接口,作为Hertz代码生成的入口点。

这里使用了Apache Thrift作为IDL(接口定义语言),定义了令牌删除接口的请求和响应结构。Thrift的优势在于:

  • 跨语言支持
  • 自动代码生成
  • 强类型约束
  • 高效的序列化

2. API网关层

接口定义-openapiauth.go文件详细分析

文件位置:backend/api/model/permission/openapiauth/openapiauth.go
核心代码:

type DeletePersonalAccessTokenAndPermissionRequest struct {// PAT IdID int64 `thrift:"id,1,required" form:"id,required" json:"id,string,required" query:"id,required"`
}type DeletePersonalAccessTokenAndPermissionResponse struct {Code int32  `thrift:"code,1,required" form:"code,required" json:"code,required" query:"code,required"`Msg  string `thrift:"msg,2,required" form:"msg,required" json:"msg,required" query:"msg,required"`
}type OpenAPIAuthService interface {DeletePersonalAccessTokenAndPermission(ctx context.Context, req *DeletePersonalAccessTokenAndPermissionRequest) (r *DeletePersonalAccessTokenAndPermissionResponse, err error)
}

文件作用:
由thriftgo自动生成的Go代码文件,基于IDL定义生成对应的Go结构体和接口,提供类型安全的API模型。这些结构体支持多种序列化格式(thrift、form、json、query),便于不同场景下的数据绑定。

接口处理实现-open_apiauth_service.go文件详细分析

文件位置:backend/api/handler/coze/open_apiauth_service.go

核心代码:

// DeletePersonalAccessTokenAndPermission .
// @router /api/permission_api/pat/delete_personal_access_token_and_permission [POST]
func DeletePersonalAccessTokenAndPermission(ctx context.Context, c *app.RequestContext) {var err errorvar req openapiauth.DeletePersonalAccessTokenAndPermissionRequesterr = c.BindAndValidate(&req)if err != nil {invalidParamRequestResponse(c, err.Error())return}if req.ID <= 0 {invalidParamRequestResponse(c, "invalid id")return}userID, err := ctx_cache.GetUserID(ctx)if err != nil {logs.CtxWarnf(ctx, "get user id failed, err=%v", err)unauthorizedRequestResponse(c)return}resp, err := openapiauthApp.OpenAuthApplication.DeletePersonalAccessTokenAndPermission(ctx, &openapiauth.DeletePersonalAccessTokenAndPermissionRequest{ID: req.ID,})if err != nil {logs.CtxErrorf(ctx, "OpenAuthApplication.DeletePersonalAccessTokenAndPermission failed, err=%v", err)internalServerErrorResponse(ctx, c, err)return}c.JSON(consts.StatusOK, resp)
}

文件作用:
该文件是通过Hertz代码生成器生成的API路由处理器,负责处理HTTP请求和响应。对于删除令牌接口,主要完成以下工作:

  1. 参数绑定与验证:使用c.BindAndValidate方法将HTTP请求参数绑定到[DeletePersonalAccessTokenAndPermissionRequest](file:///D:/Coze_Qoder_1/coze-studio/backend/api/model/permission/openapiauth/openapiauth.go#L205-L208)结构体,并进行基础验证。

  2. 参数检查:检查传入的ID是否有效(大于0)。

  3. 用户身份验证:通过ctx_cache.GetUserID从上下文中获取当前用户ID,确保操作的安全性。

  4. 业务逻辑调用:调用应用服务层的[DeletePersonalAccessTokenAndPermission](file:///D:/Coze_Qoder_1/coze-studio/backend/application/openauth/open_auth_app.go#L59-L63)方法执行实际的删除操作。

  5. 响应处理:根据业务逻辑执行结果,返回相应的HTTP响应。

路由注册:
通过注释@router /api/permission_api/pat/delete_personal_access_token_and_permission [POST]实现路由注册,将HTTP POST请求映射到[DeletePersonalAccessTokenAndPermission](file:///D:/Coze_Qoder_1/coze-studio/backend/api/handler/coze/open_apiauth_service.go#L36-L65)函数处理。

这种设计体现了关注点分离原则,路由处理器只负责HTTP层面的处理,具体的业务逻辑交给应用服务层处理。

路由注册实现-api.go文件详细分析

文件位置:backend/api/router/coze/api.go
核心代码:

// Code generated by hertz generator. DO NOT EDIT.func Register(r *server.Hertz) {root := r.Group("/", rootMw()...){_api := root.Group("/api", _apiMw()...){_permission_api := _api.Group("/permission_api", _permission_apiMw()...){_pat := _permission_api.Group("/pat", _patMw()...)_pat.POST("/delete_personal_access_token_and_permission", append(_deletepersonalaccesstokenandpermissionMw(), coze.DeletePersonalAccessTokenAndPermission)...)}}}
}

文件作用:
此文件是Coze Studio后端的核心路由注册文件,由hertz generator自动生成,负责将所有HTTP API接口路由与对应的处理函数进行绑定和注册。该文件构建了完整的RESTful API路由树结构。对于API授权令牌删除功能,构建了专门的路由结构:

/api/permission_api/pat/delete_personal_access_token_and_permission [POST]
├── rootMw() # 根级中间件
├── _apiMw() # API组中间件
├── _permission_apiMw() # 权限API组中间件
├── _patMw() # PAT模块中间件
├── _deletepersonalaccesstokenandpermissionMw() # 删除令牌接口专用中间件
└── coze.DeletePersonalAccessTokenAndPermission # 删除令牌处理函数

路由注册详细分析:

  1. 路由分组:系统采用分层路由设计,将路由按功能模块进行分组管理。删除令牌接口位于/api/permission_api/pat路径下,体现了其属于API权限管理模块中的个人访问令牌子模块。

  2. HTTP方法:删除令牌接口使用POST方法,符合RESTful设计规范中对资源删除操作的处理方式,确保操作的安全性。

  3. 中间件机制:路由注册时会附加多个层级的中间件:

    • 根级中间件:处理所有请求的通用逻辑
    • API组中间件:处理API相关请求的通用逻辑
    • 权限API组中间件:处理权限相关请求的通用逻辑
    • PAT模块中间件:处理个人访问令牌相关请求的通用逻辑
    • 删除令牌接口专用中间件:处理删除令牌操作的特定逻辑
  4. 处理函数绑定:将/delete_personal_access_token_and_permission路径与[coze.DeletePersonalAccessTokenAndPermission](file:///D:/Coze_Qoder_1/coze-studio/backend/api/handler/coze/open_apiauth_service.go#L36-L65)处理函数进行绑定,确保请求能被正确处理。

这种路由注册机制的优势:

  • 模块化管理:通过路由分组实现功能模块的清晰划分
  • 安全控制:通过多层中间件机制确保请求的安全性验证
  • 易于扩展:支持在不同层级添加相应的处理逻辑
  • 性能优化:仅对需要的路由应用相应的中间件

中间件系统-middleware.go文件详细分析

文件位置:backend/api/router/coze/middleware.go
核心代码:

func _permission_apiMw() []app.HandlerFunc {// API权限管理模块中间件return nil
}func _patMw() []app.HandlerFunc {// Personal Access Token模块中间件return nil
}func _deletepersonalaccesstokenandpermissionMw() []app.HandlerFunc {// 删除个人访问令牌接口专用中间件return nil
}

文件作用:

  1. 中间件函数定义:为API授权模块的每个路由组和特定路由提供中间件挂载点,其中[_deletepersonalaccesstokenandpermissionMw()](file:///D:/Coze_Qoder_1/coze-studio/backend/api/router/coze/middleware.go#L71-L73)专门用于处理删除个人访问令牌的请求。

  2. 路由层级管理:按照路由的层级结构组织中间件函数,支持三层中间件架构。删除令牌功能的中间件按以下顺序执行:

    • 根级中间件:处理所有请求的通用逻辑
    • API组中间件:处理API相关请求的通用逻辑
    • 权限API组中间件:处理权限相关请求的通用逻辑
    • PAT模块中间件:处理个人访问令牌相关请求的通用逻辑
    • 删除令牌接口专用中间件:处理删除令牌操作的特定逻辑
  3. 开发者扩展接口:提供统一的接口供开发者添加自定义中间件逻辑,如认证、鉴权、限流、日志记录等。对于删除令牌接口,可以在此处添加:

    • 请求频率限制
    • 操作日志记录
    • 安全审计检查
    • 参数合法性验证
  4. 粒度化控制:支持从模块级别到接口级别的细粒度中间件控制。删除令牌功能可以独立配置其专用中间件,而不会影响其他API接口。

删除令牌接口中间件执行流程:

请求到达
↓
rootMw() 根级中间件
↓
_apiMw() API组中间件
↓
_permission_apiMw() 权限API组中间件
↓
_patMw() PAT模块中间件
↓
_deletepersonalaccesstokenandpermissionMw() 删除令牌接口专用中间件
↓
coze.DeletePersonalAccessTokenAndPermission 处理函数

这种中间件设计的优势:

  • 安全增强:可以在不同层级实施不同的安全策略
  • 灵活配置:针对特定接口可以添加专门的安全检查和日志记录
  • 性能优化:仅在必要时执行相应的检查逻辑
  • 易于维护:各层中间件职责明确,便于独立维护和更新

3. 应用服务层

应用服务初始化

文件位置:backend/application/openauth/init.go

核心代码:

type OpenAuthApplicationService struct {OpenAPIDomainSVC openapi.APIAuth
}var OpenAuthApplication = &OpenAuthApplicationService{}func InitService(db *gorm.DB, idGenSVC idgen.IDGenerator) *OpenAuthApplicationService {// 初始化领域服务openapiAuthDomainSVC = openapiauth2.NewService(&openapiauth2.Components{IDGen: idGenSVC,DB:    db,})// 注入依赖到应用服务OpenAuthApplication.OpenAPIDomainSVC = openapiAuthDomainSVCreturn OpenAuthApplication
}

文件作用:

依赖注入:接收数据库连接和ID生成器作为参数
领域服务创建:通过NewService工厂方法创建API认证领域服务
依赖配置:将领域服务注入到应用服务实例中
单例返回:返回全局唯一的应用服务实例
这种初始化模式的优势:

依赖注入:通过构造函数注入,便于测试和维护
工厂模式:使用工厂方法创建复杂对象,隐藏创建细节
单例模式:确保服务的全局唯一性,节省内存开销
层次分离:应用层只依赖领域层接口,不直接依赖基础设施层

应用服务实现

文件位置:backend/application/openauth/openapiauth.go

核心代码:

func (s *OpenAuthApplicationService) DeletePersonalAccessTokenAndPermission(ctx context.Context, req *openapimodel.DeletePersonalAccessTokenAndPermissionRequest) (*openapimodel.DeletePersonalAccessTokenAndPermissionResponse, error) {resp := new(openapimodel.DeletePersonalAccessTokenAndPermissionResponse)userID := ctxutil.GetUIDFromCtx(ctx)appReq := &entity.DeleteApiKey{ID:     req.ID,UserID: *userID,}err := openapiAuthDomainSVC.Delete(ctx, appReq)if err != nil {logs.CtxErrorf(ctx, "OpenAuthApplicationService.DeletePersonalAccessTokenAndPermission failed, err=%v", err)return resp, errors.New("DeletePersonalAccessTokenAndPermission failed")}return resp, nil
}

文件作用:
应用服务层作为领域服务层与API网关层的桥梁,主要负责以下工作:

  1. 获取用户ID:通过ctxutil.GetUIDFromCtx(ctx)从上下文中获取当前用户的ID,确保操作的安全性。

  2. 调用领域服务:将请求参数转换为领域实体对象[DeleteApiKey]

  3. 结果处理:根据领域服务层的执行结果,构建并返回响应对象。

应用服务层的设计遵循了领域驱动设计(DDD)的原则,它不包含核心业务逻辑,而是协调领域服务完成业务操作,并处理不同层之间的数据转换。

4. 领域服务层

领域服务接口定义-api_auth.go文件详细分析

文件位置:backend/domain/openauth/openapiauth/api_auth.go

核心代码:

type APIAuth interface {Delete(ctx context.Context, req *entity.DeleteApiKey) (*entity.ApiKey, error)
}

文件作用:
定义了API授权领域服务的接口规范,[Delete]方法用于删除指定的API密钥。通过接口定义,实现了依赖倒置原则,使得应用服务层依赖于抽象而非具体实现。

领域服务实现-api_auth_impl.go文件详细分析

文件位置:backend/domain/openauth/openapiauth/api_auth_impl.go

核心代码:

func (a *apiAuthImpl) Delete(ctx context.Context, req *entity.DeleteApiKey) (*entity.ApiKey, error) {err := a.dao.Delete(ctx, req.ID, req.UserID)if err != nil {return nil, err}return nil, nil
}

文件作用:
[apiAuthImpl]结构体实现了[APIAuth]接口,是API授权领域服务的具体实现。在删除API密钥的实现中,主要完成以下工作:

  1. 调用数据访问层的[Delete]方法执行实际的数据库删除操作。

  2. 根据执行结果返回相应的结果或错误。

领域服务层封装了核心业务逻辑,与具体的存储实现解耦,使得业务逻辑独立且可测试。

领域实体定义-entity.go文件详细分析

文件位置:backend/domain/openauth/openapiauth/entity/api_auth.go

核心代码:

type DeleteApiKey struct {ID     int64 `json:"id"`UserID int64 `json:"user_id"`
}

文件作用:
定义了API授权相关的领域实体。[DeleteApiKey]实体用于封装删除API密钥操作所需的参数,包括API密钥ID和用户ID,确保删除操作只能由密钥的所有者执行。

5. 数据访问层

数据访问接口定义-api_key.go文件详细分析

文件位置:backend/domain/openauth/openapiauth/internal/dal/api_key.go

核心代码:

func (a *ApiKeyDAO) Delete(ctx context.Context, id int64, userID int64) error {_, err := a.dbQuery.APIKey.WithContext(ctx).Where(a.dbQuery.APIKey.ID.Eq(id)).Where(a.dbQuery.APIKey.UserID.Eq(userID)).Delete()return err
}

文件作用:
[ApiKeyDAO]结构体封装了对API密钥表的所有数据访问操作。在删除API密钥的实现中,主要完成以下工作:

  1. 使用a.dbQuery.APIKey.WithContext(ctx)创建带上下文的查询对象。

  2. 使用Where方法添加过滤条件,确保只能删除指定ID且属于当前用户的API密钥,增强安全性。

  3. 调用Delete()方法执行实际的删除操作。

数据访问层通过[gen]工具生成的代码,提供了类型安全的数据库操作接口,避免了手写SQL可能带来的错误。

数据模型定义-model.go文件详细分析

文件位置:backend/domain/openauth/openapiauth/internal/dal/model/api_key.gen.go

核心代码:

type APIKey struct {ID         int64  `gorm:"column:id;primaryKey;autoIncrement:true;comment:Primary Key ID" json:"id"`                              // Primary Key IDAPIKey     string `gorm:"column:api_key;not null;comment:API Key hash" json:"api_key"`                                           // API Key hashName       string `gorm:"column:name;not null;comment:API Key Name" json:"name"`                                                 // API Key NameStatus     int32  `gorm:"column:status;not null;comment:0 normal, 1 deleted" json:"status"`                                      // 0 normal, 1 deletedUserID     int64  `gorm:"column:user_id;not null;comment:API Key Owner" json:"user_id"`                                          // API Key OwnerExpiredAt  int64  `gorm:"column:expired_at;not null;comment:API Key Expired Time" json:"expired_at"`                             // API Key Expired TimeCreatedAt  int64  `gorm:"column:created_at;not null;autoCreateTime:milli;comment:Create Time in Milliseconds" json:"created_at"` // Create Time in MillisecondsUpdatedAt  int64  `gorm:"column:updated_at;not null;autoUpdateTime:milli;comment:Update Time in Milliseconds" json:"updated_at"` // Update Time in MillisecondsLastUsedAt int64  `gorm:"column:last_used_at;not null;comment:Used Time in Milliseconds" json:"last_used_at"`                    // Used Time in Milliseconds
}

文件作用:
该文件定义了与数据库表对应的模型结构体。通过GORM的标签定义,实现了结构体字段与数据库表字段的映射关系。同时,通过注释清晰地描述了每个字段的含义和用途。

API密钥查询方法
  • 基于 APIKey 模型生成查询结构体
  • 包含 aPIKey 结构体和 IAPIKeyDo 接口
  • 生成所有 CRUD 方法和查询构建器
  • 支持类型安全的数据库操作和复杂查询
    文件位置:backend/domain/openauth/openapiauth/internal/dal/query/api_key.gen.go
    示例代码:
// Code generated by gorm.io/gen. DO NOT EDIT.package queryimport ("context""gorm.io/gorm""gorm.io/gorm/clause""gorm.io/gorm/schema""gorm.io/gen""gorm.io/gen/field""gorm.io/plugin/dbresolver""github.com/coze-dev/coze-studio/backend/domain/openauth/openapiauth/internal/dal/model"
)// newAPIKey 创建API密钥查询对象的工厂方法
func newAPIKey(db *gorm.DB, opts ...gen.DOOption) aPIKey {_aPIKey := aPIKey{}_aPIKey.aPIKeyDo.UseDB(db, opts...)_aPIKey.aPIKeyDo.UseModel(&model.APIKey{})tableName := _aPIKey.aPIKeyDo.TableName()_aPIKey.ALL = field.NewAsterisk(tableName)_aPIKey.ID = field.NewInt64(tableName, "id")_aPIKey.APIKey = field.NewString(tableName, "api_key")_aPIKey.Name = field.NewString(tableName, "name")_aPIKey.Status = field.NewInt32(tableName, "status")_aPIKey.UserID = field.NewInt64(tableName, "user_id")_aPIKey.ExpiredAt = field.NewInt64(tableName, "expired_at")_aPIKey.CreatedAt = field.NewInt64(tableName, "created_at")_aPIKey.UpdatedAt = field.NewInt64(tableName, "updated_at")_aPIKey.LastUsedAt = field.NewInt64(tableName, "last_used_at")_aPIKey.fillFieldMap()return _aPIKey
}// aPIKey API密钥查询结构体
type aPIKey struct {aPIKeyDoALL        field.AsteriskID         field.Int64  // Primary Key IDAPIKey     field.String // API Key hashName       field.String // API Key NameStatus     field.Int32  // 0 normal, 1 deletedUserID     field.Int64  // API Key OwnerExpiredAt  field.Int64  // API Key Expired TimeCreatedAt  field.Int64  // Create Time in MillisecondsUpdatedAt  field.Int64  // Update Time in MillisecondsLastUsedAt field.Int64  // Used Time in MillisecondsfieldMap map[string]field.Expr
}// IAPIKeyDo API密钥数据操作接口
type IAPIKeyDo interface {gen.SubQueryDebug() IAPIKeyDoWithContext(ctx context.Context) IAPIKeyDoWithResult(fc func(tx gen.Dao)) gen.ResultInfoReplaceDB(db *gorm.DB)ReadDB() IAPIKeyDoWriteDB() IAPIKeyDoAs(alias string) gen.DaoSession(config *gorm.Session) IAPIKeyDoColumns(cols ...field.Expr) gen.ColumnsClauses(conds ...clause.Expression) IAPIKeyDoNot(conds ...gen.Condition) IAPIKeyDoOr(conds ...gen.Condition) IAPIKeyDoSelect(conds ...field.Expr) IAPIKeyDoWhere(conds ...gen.Condition) IAPIKeyDoOrder(conds ...field.Expr) IAPIKeyDoDistinct(cols ...field.Expr) IAPIKeyDoOmit(cols ...field.Expr) IAPIKeyDoJoin(table schema.Tabler, on ...field.Expr) IAPIKeyDoLeftJoin(table schema.Tabler, on ...field.Expr) IAPIKeyDoRightJoin(table schema.Tabler, on ...field.Expr) IAPIKeyDoGroup(cols ...field.Expr) IAPIKeyDoHaving(conds ...gen.Condition) IAPIKeyDoLimit(limit int) IAPIKeyDoOffset(offset int) IAPIKeyDoCount() (count int64, err error)Scopes(funcs ...func(gen.Dao) gen.Dao) IAPIKeyDoUnscoped() IAPIKeyDoCreate(values ...*model.APIKey) errorCreateInBatches(values []*model.APIKey, batchSize int) errorSave(values ...*model.APIKey) errorFirst() (*model.APIKey, error)Take() (*model.APIKey, error)Last() (*model.APIKey, error)Find() ([]*model.APIKey, error)FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.APIKey, err error)FindInBatches(result *[]*model.APIKey, batchSize int, fc func(tx gen.Dao, batch int) error) errorPluck(column field.Expr, dest interface{}) errorDelete(...*model.APIKey) (info gen.ResultInfo, err error)}

API密钥查询方法特点

  1. 类型安全查询:基于gorm.io/gen自动生成,编译期检查类型安全性
  2. 完整CRUD支持:提供Create、Read、Update、Delete的完整数据库操作
  3. 高级查询功能:支持Join、Group、Having、Order等复杂查询条件
  4. 分页查询优化:内置FindByPage和ScanByPage方法,支持高效分页
  5. 读写分离:提供ReadDB()和WriteDB()方法,支持数据库读写分离
  6. 事务支持:集成GORM事务管理,确保数据一致性
  7. 链式调用:支持方法链式调用,提升开发体验
  8. 软删除支持:通过Status字段实现软删除机制

这个查询构建器是API授权-获取令牌列表功能的数据访问基础,为上层业务逻辑提供了类型安全、高性能的数据库操作接口。

在 ApiKeyDAO.Delete 方法中,调用链如下:

a.dbQuery.APIKey.WithContext(ctx) - 调用WithContext方法设置上下文
Where(a.dbQuery.APIKey.ID.Eq(id)) - 使用Where方法和ID字段添加ID相等条件
Where(a.dbQuery.APIKey.UserID.Eq(userID)) - 使用Where方法和UserID字段添加用户ID相等条件
Delete() - 调用Delete方法执行删除操作

API授权统一查询入口生成
  • 生成API授权模块的统一查询入口文件
  • 包含 Query 结构体,聚合API密钥查询对象
  • 提供 SetDefault、Use、WithContext 等核心方法
  • 实现读写分离:ReadDB() 和 WriteDB()
  • 支持事务管理和上下文传递
    文件位置:backend/domain/openauth/openapiauth/internal/dal/query/gen.go
    示例代码:
// Code generated by gorm.io/gen. DO NOT EDIT.package queryimport ("context""database/sql""gorm.io/gorm""gorm.io/gen""gorm.io/plugin/dbresolver"
)// 全局查询变量定义
var (Q      = new(Query)  // 全局查询实例APIKey *aPIKey       // API密钥查询对象
)// SetDefault 设置默认数据库连接和配置
func SetDefault(db *gorm.DB, opts ...gen.DOOption) {*Q = *Use(db, opts...)APIKey = &Q.APIKey
}// Use 创建新的查询实例
func Use(db *gorm.DB, opts ...gen.DOOption) *Query {return &Query{db:     db,APIKey: newAPIKey(db, opts...),}
}// Query API授权模块的主查询结构体
type Query struct {db *gorm.DBAPIKey aPIKey // API密钥查询对象
}// Available 检查数据库连接是否可用
func (q *Query) Available() bool { return q.db != nil }// clone 克隆查询实例使用新的数据库连接
func (q *Query) clone(db *gorm.DB) *Query {return &Query{db:     db,APIKey: q.APIKey.clone(db),}
}// ReadDB 获取读库连接的查询实例
func (q *Query) ReadDB() *Query {return q.ReplaceDB(q.db.Clauses(dbresolver.Read))
}// WriteDB 获取写库连接的查询实例
func (q *Query) WriteDB() *Query {return q.ReplaceDB(q.db.Clauses(dbresolver.Write))
}// ReplaceDB 更换数据库连接
func (q *Query) ReplaceDB(db *gorm.DB) *Query {return &Query{db:     db,APIKey: q.APIKey.replaceDB(db),}
}// queryCtx 上下文查询结构体
type queryCtx struct {APIKey IAPIKeyDo
}// WithContext 创建带上下文的查询实例
func (q *Query) WithContext(ctx context.Context) *queryCtx {return &queryCtx{APIKey: q.APIKey.WithContext(ctx),}
}// Transaction 执行事务操作
func (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error {return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.clone(tx)) }, opts...)
}// Begin 开始事务
func (q *Query) Begin(opts ...*sql.TxOptions) *QueryTx {tx := q.db.Begin(opts...)return &QueryTx{Query: q.clone(tx), Error: tx.Error}
}// QueryTx 事务查询结构体
type QueryTx struct {*QueryError error
}// Commit 提交事务
func (q *QueryTx) Commit() error {return q.db.Commit().Error
}// Rollback 回滚事务
func (q *QueryTx) Rollback() error {return q.db.Rollback().Error
}// SavePoint 保存事务保存点
func (q *QueryTx) SavePoint(name string) error {return q.db.SavePoint(name).Error
}// RollbackTo 回滚到指定保存点
func (q *QueryTx) RollbackTo(name string) error {return q.db.RollbackTo(name).Error
}

API授权统一查询入口特点

  1. 单一职责原则:专门为API授权模块设计,只管理API密钥相关的数据操作
  2. 全局变量管理:提供 Q 和 APIKey 全局变量,方便在任意地方访问
  3. 读写分离支持:通过 ReadDB() 和 WriteDB() 实现数据库读写分离
  4. 事务管理:提供 Transaction、Begin、Commit、Rollback 等完整事务操作
  5. 上下文传递:支持 WithContext 方法,便于追踪和取消操作
  6. 连接管理:支持数据库连接的克隆和更换,确保线程安全
  7. 错误处理:事务操作包含错误状态,便于错误处理和调试
  8. 代码生成:基于gorm.io/gen自动生成,确保代码的一致性和可靠性

这个文件是整个查询系统的入口点,定义了主要的数据结构和查询上下文。

关键数据结构:
Query:主查询结构体,聚合了所有表的查询对象
queryCtx:带上下文的查询结构体,用于在特定上下文中执行查询
QueryTx:事务查询结构体,用于处理数据库事务

关键函数:
WithContext:创建带上下文的查询实例,将上下文传递给具体的表查询对象
Use:创建新的查询实例,初始化所有表的查询对象
SetDefault:设置默认数据库连接和配置

这个统一查询入口是API授权-获取令牌列表功能的数据访问层核心,为上层业务逻辑提供了统一、高效、类型安全的数据库操作接口。

6.基础设施层

database.go文件详解

文件位置:backend/infra/contract/orm/database.go
核心代码:

package ormimport ("gorm.io/gorm"
)type DB = gorm.DB

文件作用:

  • 定义了 type DB = gorm.DB ,为 GORM 数据库对象提供类型别名
  • 作为契约层(Contract),为上层提供统一的数据库接口抽象
  • 便于后续可能的数据库实现替换(如从 MySQL 切换到 PostgreSQL)

mysql.go文件详解

文件位置:backend/infra/impl/mysql/mysql.go
核心代码:

package mysqlimport ("fmt""os""gorm.io/driver/mysql""gorm.io/gorm"
)func New() (*gorm.DB, error) {dsn := os.Getenv("MYSQL_DSN")db, err := gorm.Open(mysql.Open(dsn))if err != nil {return nil, fmt.Errorf("mysql open, dsn: %s, err: %w", dsn, err)}return db, nil
}

文件作用:

  • 定义了 New() 函数,负责建立 GORM MySQL 数据库连接
  • 使用环境变量 MYSQL_DSN 配置数据库连接字符串
  • 返回 *gorm.DB 实例,作为整个应用的数据库连接对象
  • 后端服务启动时,调用 mysql.New() 初始化数据库连接
main.go → application.Init() → appinfra.Init() → mysql.New()

gen_orm_query.go文件详解

文件位置:backend/types/ddl/gen_orm_query.go
核心代码:

// 用户领域查询生成配置
"domain/openauth/openapiauth/internal/dal/query": {"api_key": {},}

文件作用:
自动生成类型安全的查询接口:
为api_key表生成aPIKey查询结构体
生成对应的IAPIKeyDo接口,包含所有CRUD操作方法

支持令牌列表查询需求:
生成的代码支持按用户ID过滤查询
支持分页查询(Limit、Offset)
支持排序查询(Order by CreatedAt)
支持条件查询(Where UserID.Eq())

文件依赖关系

依赖层次:
数据库表结构 (schema.sql)↓    gen_orm_query.go
模型文件 (model/api_key.gen.go) - 模型先生成↓
查询文件 (query/api_key.gen.go) - 依赖对应模型↓
统一入口 (query/gen.go) - 依赖所有查询文件

重新生成注意事项

  • 清理旧文件:生成前会自动删除所有 .gen.go 文件
  • 数据库连接:确保 MySQL 服务运行且包含最新表结构
  • 依赖顺序:GORM Gen 自动处理文件间的依赖关系
  • 原子操作:整个生成过程是原子的,要么全部成功要么全部失败

这种分层生成机制确保了代码的一致性和类型安全,同时通过依赖关系保证了生成文件的正确性。

7.数据存储层-MYSQL数据库表

MySQL数据库设计

PAT信息存储在两个主要表中:

  1. pat_table - 存储PAT基本信息

    • id: PAT ID (主键)
    • user_id: 用户ID
    • token: 加密后的令牌
    • name: 令牌名称
    • expire_time: 过期时间
    • create_time: 创建时间
    • update_time: 更新时间
  2. pat_permission_table - 存储PAT权限信息

    • id: 权限ID (主键)
    • pat_id: 关联的PAT ID
    • resource_type: 资源类型
    • resource_id: 资源ID
    • permission_type: 权限类型
    • create_time: 创建时间

8. 核心流程分析

删除令牌完整处理流程

API授权令牌删除功能的核心处理流程如下:

┌─────────────────────────────────────────────────────────────┐
│                    API网关层                                 │
│  ┌─────────────────────────────────────────────────────┐    │
│  │    DeletePersonalAccessTokenAndPermission           │    │
│  │  1.接收HTTP请求                                      │    │
│  │  2.参数绑定和验证                                    │    │
│  │  3.获取用户身份                                      │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                   应用服务层                                 │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  OpenAuthApplicationService.                        │    │
│  │  DeletePersonalAccessTokenAndPermission             │    │
│  │  4.调用领域服务Delete方法                            │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                   领域服务层                                 │
│  ┌─────────────────────────────────────────────────────┐    │
│  │        apiAuthImpl.Delete                           │    │
│  │  5.调用数据访问层Delete方法                          │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                   数据访问层                                 │
│  ┌─────────────────────────────────────────────────────┐    │
│  │         ApiKeyDAO.Delete                            │    │
│  │  6.构造安全的删除SQL语句                             │    │
│  │  7.执行数据库删除操作                                │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                    API网关层                                 │
│  ┌─────────────────────────────────────────────────────┐    │
│  │    DeletePersonalAccessTokenAndPermission           │    │
│  │  8.返回HTTP响应                                      │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘

整个流程体现了清晰的分层架构设计:

  1. API网关层负责处理HTTP协议相关的工作,如请求接收、参数验证、身份认证和响应返回。
  2. 应用服务层作为协调者,负责业务流程的编排,但不包含核心业务逻辑。
  3. 领域服务层封装了核心业务逻辑,确保业务规则的正确实现。
  4. 数据访问层负责与数据库交互,提供安全、高效的数据操作。

这种分层设计的优势:

  • 职责清晰,各层只关注自己的核心功能
  • 便于测试,可以针对不同层编写单元测试
  • 易于维护,修改某一层实现不会影响其他层
  • 可扩展性强,可以根据需求灵活调整各层实现

删除令牌功能流程总结

Coze Studio项目中删除令牌功能的完整执行流程如下:

  1. 客户端发送删除请求,携带PAT ID
  2. API网关层接收请求并解析参数
  3. 校验请求参数有效性
  4. 获取用户身份信息
  5. 构建应用层请求并调用应用服务
  6. 应用服务层进行业务逻辑处理:
    • 获取PAT详细信息
    • 校验PAT存在性和用户权限
    • 调用领域服务删除PAT
    • 删除相关权限
  7. 领域服务层协调数据访问层操作
  8. 数据访问层执行数据库操作
  9. 构建响应并返回给客户端

整个流程体现了Coze Studio后端架构的分层设计思想,各层之间通过定义良好的接口进行通信,实现了高内聚低耦合的设计原则。通过这种分层架构,使得删除令牌功能具有良好的可维护性和可扩展性。

9. 安全机制分析

用户身份验证与权限控制

在API授权令牌删除过程中,系统实现了多层次的安全机制:

  1. 用户身份验证

    • 在API网关层,通过ctx_cache.GetUserID(ctx)获取当前用户ID
    • 确保只有已登录用户才能执行删除操作
  2. 权限控制

    • 在应用服务层,将用户ID作为参数传递给领域服务
    • 在数据访问层,通过.Where(a.dbQuery.APIKey.UserID.Eq(userID))确保用户只能删除自己创建的API密钥
  3. 参数验证

    • 在API网关层对请求参数进行基础验证
    • 检查ID的有效性(必须大于0)

这些安全机制确保了API密钥删除操作的安全性,防止未授权访问和越权操作。

数据安全

  1. 软删除机制
    通过[Status](file:///D:/Coze_Qoder_1/coze-studio/backend/domain/openauth/openapiauth/internal/dal/query/api_key.gen.go#L58-L58)字段实现软删除,将状态从0(正常)改为1(已删除),而不是直接从数据库中物理删除记录。这种方式的优势包括:

    • 可以恢复误删除的数据
    • 保留操作历史,便于审计
    • 避免因外键关联导致的删除异常
  2. 敏感信息保护
    数据库中存储的是API密钥的哈希值而非明文,即使数据库被非法访问,也无法直接获取用户的API密钥明文。

SQL注入防护

通过使用[gen](file:///D:/Coze_Qoder_1/coze-studio/backend/domain/openauth/openapiauth/internal/dal/query/gen.go#L19-L23)工具生成的类型安全的查询接口,避免了手写SQL可能带来的SQL注入风险。所有参数都通过预编译的方式安全地传递给数据库。

10. 错误处理与安全性考虑

在删除令牌功能的实现中,系统考虑了多种错误情况和安全因素:

  1. 参数校验:对所有输入参数进行有效性校验,防止非法参数导致的错误
  2. 权限验证:确保用户只能删除自己拥有的PAT,防止越权访问
  3. 数据库事务:使用事务保证PAT和相关权限删除的原子性
  4. 日志记录:记录关键操作日志,便于审计和问题追踪
  5. 错误码设计:定义明确的错误码,便于客户端处理不同错误情况
  6. 异常处理:对可能出现的异常情况进行捕获和处理

11. 性能优化建议

针对删除令牌功能,可以考虑以下性能优化措施:

  1. 缓存PAT信息:对于频繁查询的PAT信息可以使用缓存,减少数据库访问
  2. 异步删除:对于非关键操作可以采用异步处理,提高响应速度
  3. 批量删除:支持批量删除操作,减少网络往返
  4. 索引优化:在数据库中为常用查询字段建立合适的索引
  5. 连接池:使用数据库连接池提高数据库访问效率

12. 核心技术特点

领域驱动设计(DDD)

项目采用了领域驱动设计思想,将API授权令牌删除功能划分为不同的层次:

  • API网关层:处理HTTP协议相关工作
  • 应用服务层:协调业务流程
  • 领域服务层:实现核心业务逻辑
  • 数据访问层:处理数据持久化

这种设计使得业务逻辑与技术实现分离,提高了代码的可维护性和可测试性。

依赖注入

通过init.go文件实现依赖注入,使得各层之间松耦合,便于单元测试和后续的功能扩展。

类型安全的数据库访问

使用gen工具生成类型安全的数据库访问代码,避免了手写SQL可能带来的错误和安全风险。

安全设计

  • 多层次身份验证和权限控制
  • 软删除机制保护数据安全
  • 敏感信息哈希存储
  • SQL注入防护

总结

通过对Coze Studio项目中API授权令牌删除功能的源码分析,我们可以看到一个典型的分层架构设计在实际项目中的应用。整个删除流程从API网关层接收请求开始,经过应用服务层协调,领域服务层处理核心业务逻辑,最终由数据访问层完成数据操作,层次清晰、职责分明。

系统在安全性方面也做了充分考虑,通过多层次的身份验证、权限控制和数据保护机制,确保了API密钥删除操作的安全性。同时,使用现代化的开发工具和框架,如Hertz、GORM和gen,提高了开发效率和代码质量。

这种设计模式和实现方式为类似的功能开发提供了良好的参考,体现了高质量软件工程实践在实际项目中的应用.

http://www.xdnf.cn/news/19408.html

相关文章:

  • 【15】VisionMaster入门到精通——--通信--TCP通信、UDP通信、串口通信、PLC通信、ModBus通信
  • 鸿蒙ArkTS 核心篇-16-循环渲染(组件)
  • lvgl模拟器 被放大 导致显示模糊问题
  • Notepad++使用技巧1
  • 日志ELK、ELFK、EFK
  • 快速学习和掌握Jackson 、Gson、Fastjson
  • AI + 行业渗透率报告:医疗诊断、工业质检领域已进入规模化落地阶段
  • GD32入门到实战20--定时器
  • 【LeetCode】大厂面试算法真题回忆(122) —— 篮球比赛
  • react性能优化有哪些
  • SSR降级CSR:高可用容灾方案详解
  • Android中handler机制
  • 【Android】JSONObject和Gson的使用
  • HTTP的概念、原理、工作机制、数据格式和REST
  • 《C++——makefile》
  • 三重积分的性质
  • 【MATLAB绘图进阶教程】(2-6)动态绘图制作详解与例程,包括drawnow、pause、getframe、video write等命令
  • 机器学习时间序列算法进行随机划分数据是不合适的!
  • Dify1.8.0最新版本安装教程:Ubuntu25.04系统本地化安装部署Dify详细教程
  • 移动零,leetCode热题100,C++实现
  • IP-Guard支持修改安全区域密级文字和密级级数
  • 嵌入式学习日记(38)HTTP
  • Java学习笔记-多线程基础
  • Kafka 4.0 生产者配置全解析与实战调优
  • Go语言流式输出实战:构建高性能实时应用
  • 数据结构(力扣刷题)
  • 蜂窝通信模组OpenCPU的介绍
  • REST-assured获取响应数据详解
  • 手写链路追踪优化-自动全局追踪代替局部手动追踪
  • 做一个实用的节假日工具