docker匿名Token缓存

This commit is contained in:
user123456
2025-06-11 14:18:49 +08:00
parent 08415b4034
commit a2550bc80e
4 changed files with 166 additions and 1 deletions

View File

@@ -47,6 +47,12 @@ type AppConfig struct {
// 新增Registry映射配置
Registries map[string]RegistryMapping `toml:"registries"`
// Token缓存配置
TokenCache struct {
Enabled bool `toml:"enabled"` // 是否启用token缓存
DefaultTTL string `toml:"defaultTTL"` // 默认缓存时间
} `toml:"tokenCache"`
}
var (
@@ -118,6 +124,13 @@ func DefaultConfig() *AppConfig {
Enabled: true,
},
},
TokenCache: struct {
Enabled bool `toml:"enabled"`
DefaultTTL string `toml:"defaultTTL"`
}{
Enabled: true, // docker认证的匿名Token缓存配置用于提升性能
DefaultTTL: "20m",
},
}
}

View File

@@ -81,3 +81,10 @@ enabled = true
# authHost = "harbor.company.com/service/token"
# authType = "basic"
# enabled = false
# docker的匿名Token缓存配置显著提升性能
[tokenCache]
# 是否启用token缓存
enabled = true
# 默认缓存时间自动从响应中提取实际TTL
defaultTTL = "20m"

View File

@@ -305,8 +305,70 @@ func handleTagsRequest(c *gin.Context, imageRef string) {
c.JSON(http.StatusOK, response)
}
// ProxyDockerAuthGin Docker认证代理
// ProxyDockerAuthGin Docker认证代理(带缓存优化)
func ProxyDockerAuthGin(c *gin.Context) {
// 检查是否启用token缓存
if isTokenCacheEnabled() {
proxyDockerAuthWithCache(c)
} else {
proxyDockerAuthOriginal(c)
}
}
// proxyDockerAuthWithCache 带缓存的认证代理
func proxyDockerAuthWithCache(c *gin.Context) {
// 1. 构建缓存key基于完整的查询参数
cacheKey := buildCacheKey(c.Request.URL.RawQuery)
// 2. 尝试从缓存获取token
if cachedToken := globalTokenCache.Get(cacheKey); cachedToken != "" {
writeTokenResponse(c, cachedToken)
return
}
// 3. 缓存未命中,创建响应记录器
recorder := &ResponseRecorder{
ResponseWriter: c.Writer,
statusCode: 200,
}
c.Writer = recorder
// 4. 调用原有认证逻辑
proxyDockerAuthOriginal(c)
// 5. 如果认证成功,缓存响应
if recorder.statusCode == 200 && len(recorder.body) > 0 {
ttl := extractTTLFromResponse(recorder.body)
globalTokenCache.Set(cacheKey, string(recorder.body), ttl)
}
// 6. 写入实际响应(如果还没写入)
if !recorder.written {
c.Writer = recorder.ResponseWriter
c.Data(recorder.statusCode, "application/json", recorder.body)
}
}
// ResponseRecorder HTTP响应记录器
type ResponseRecorder struct {
gin.ResponseWriter
statusCode int
body []byte
written bool
}
func (r *ResponseRecorder) WriteHeader(code int) {
r.statusCode = code
}
func (r *ResponseRecorder) Write(data []byte) (int, error) {
r.body = append(r.body, data...)
r.written = true
return r.ResponseWriter.Write(data)
}
// proxyDockerAuthOriginal Docker认证代理原始逻辑保持不变
func proxyDockerAuthOriginal(c *gin.Context) {
// 检查是否有目标Registry域名来自Context
var authURL string
if targetDomain, exists := c.Get("target_registry_domain"); exists {

83
src/token_cache.go Normal file
View File

@@ -0,0 +1,83 @@
package main
import (
"crypto/md5"
"encoding/json"
"fmt"
"sync"
"time"
"github.com/gin-gonic/gin"
)
// CachedToken 缓存的Token信息
type CachedToken struct {
Token string
ExpiresAt time.Time
}
// SimpleTokenCache 极简Token缓存
type SimpleTokenCache struct {
cache sync.Map // 线程安全的并发映射
}
var globalTokenCache = &SimpleTokenCache{}
// Get 获取缓存的token如果不存在或过期返回空字符串
func (c *SimpleTokenCache) Get(key string) string {
if v, ok := c.cache.Load(key); ok {
if cached := v.(*CachedToken); time.Now().Before(cached.ExpiresAt) {
return cached.Token
}
// 自动清理过期token保持内存整洁
c.cache.Delete(key)
}
return ""
}
// Set 设置token缓存自动计算过期时间
func (c *SimpleTokenCache) Set(key, token string, ttl time.Duration) {
c.cache.Store(key, &CachedToken{
Token: token,
ExpiresAt: time.Now().Add(ttl),
})
}
// buildCacheKey 构建稳定的缓存key
func buildCacheKey(query string) string {
// 使用MD5确保key的一致性和简洁性
return fmt.Sprintf("token:%x", md5.Sum([]byte(query)))
}
// extractTTLFromResponse 从响应中智能提取TTL
func extractTTLFromResponse(responseBody []byte) time.Duration {
var tokenResp struct {
ExpiresIn int `json:"expires_in"`
}
// 默认30分钟TTL确保稳定性
defaultTTL := 30 * time.Minute
if json.Unmarshal(responseBody, &tokenResp) == nil && tokenResp.ExpiresIn > 0 {
// 使用响应中的过期时间但提前5分钟过期确保安全边际
safeTTL := time.Duration(tokenResp.ExpiresIn-300) * time.Second
if safeTTL > 5*time.Minute { // 确保至少有5分钟的缓存时间
return safeTTL
}
}
return defaultTTL
}
// writeTokenResponse 写入token响应
func writeTokenResponse(c *gin.Context, cachedBody string) {
// 直接返回缓存的完整响应体,保持格式一致性
c.Header("Content-Type", "application/json")
c.String(200, cachedBody)
}
// isTokenCacheEnabled 检查token缓存是否启用
func isTokenCacheEnabled() bool {
cfg := GetConfig()
return cfg.TokenCache.Enabled
}