完善开放接口日志并修复调试页刷新
This commit is contained in:
@@ -48,6 +48,7 @@ func OpenAuth(cfg OpenAuthConfig) gin.HandlerFunc {
|
||||
c.Header("X-Request-Id", reqID)
|
||||
|
||||
if cfg.DB == nil || cfg.Codec == nil {
|
||||
openlog.Warn(c, "open_auth uninitialized")
|
||||
response.ServerError(c, "开放接口认证服务未初始化")
|
||||
c.Abort()
|
||||
return
|
||||
@@ -57,17 +58,20 @@ func OpenAuth(cfg OpenAuthConfig) gin.HandlerFunc {
|
||||
nonce := c.GetHeader("X-Nonce")
|
||||
sign := c.GetHeader("X-Sign")
|
||||
if appKey == "" || timestamp == "" || nonce == "" || sign == "" {
|
||||
openlog.Warn(c, "open_auth missing_headers app_key=%s", openlog.MaskKey(appKey))
|
||||
response.Unauthorized(c, "缺少鉴权头:X-App-Key、X-Timestamp、X-Nonce、X-Sign")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if len(nonce) < 8 || len(nonce) > 96 {
|
||||
openlog.Warn(c, "open_auth bad_nonce_len len=%d app_key=%s", len(nonce), openlog.MaskKey(appKey))
|
||||
response.Unauthorized(c, "X-Nonce 长度需在 8~96 之间")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
ts, err := strconv.ParseInt(timestamp, 10, 64)
|
||||
if err != nil || abs64(time.Now().Unix()-ts) > cfg.SkewSeconds {
|
||||
openlog.Warn(c, "open_auth expired app_key=%s ts=%s skew=%d", openlog.MaskKey(appKey), timestamp, cfg.SkewSeconds)
|
||||
response.Unauthorized(c, "请求已过期或 X-Timestamp 格式错误")
|
||||
c.Abort()
|
||||
return
|
||||
@@ -75,6 +79,7 @@ func OpenAuth(cfg OpenAuthConfig) gin.HandlerFunc {
|
||||
|
||||
bodyBytes, err := io.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
openlog.Warn(c, "open_auth read_body_fail err=%v", err)
|
||||
response.BadRequest(c, "读取请求体失败")
|
||||
c.Abort()
|
||||
return
|
||||
@@ -83,17 +88,20 @@ func OpenAuth(cfg OpenAuthConfig) gin.HandlerFunc {
|
||||
|
||||
var client model.APIClient
|
||||
if err := cfg.DB.Where("app_key = ? AND status = ?", appKey, model.APIClientStatusActive).First(&client).Error; err != nil {
|
||||
openlog.Warn(c, "open_auth invalid_key app_key=%s", openlog.MaskKey(appKey))
|
||||
response.Unauthorized(c, "无效的 API Key")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if client.ExpiresAt != nil && client.ExpiresAt.Before(time.Now()) {
|
||||
openlog.Warn(c, "open_auth key_expired app_key=%s client_id=%d", openlog.MaskKey(appKey), client.ID)
|
||||
response.Unauthorized(c, "API Key 已过期")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
secret, err := cfg.Codec.Decrypt(client.SecretCiphertext)
|
||||
if err != nil {
|
||||
openlog.Warn(c, "open_auth decrypt_fail app_key=%s client_id=%d err=%v", openlog.MaskKey(appKey), client.ID, err)
|
||||
response.ServerError(c, "API 客户端密钥不可用")
|
||||
c.Abort()
|
||||
return
|
||||
@@ -103,6 +111,10 @@ func OpenAuth(cfg OpenAuthConfig) gin.HandlerFunc {
|
||||
path := c.Request.URL.Path
|
||||
expected := BuildOpenV1Sign(secret, appKey, timestamp, nonce, method, path, bodyBytes)
|
||||
if !hmac.Equal([]byte(strings.ToLower(sign)), []byte(expected)) {
|
||||
bodyHash := sha256.Sum256(bodyBytes)
|
||||
openlog.Warn(c, "open_auth sign_mismatch app_key=%s method=%s path=%s body_sha256=%s sign=%s expected=%s",
|
||||
openlog.MaskKey(appKey), method, path, hex.EncodeToString(bodyHash[:]),
|
||||
openlog.MaskSign(sign), openlog.MaskSign(expected))
|
||||
response.Unauthorized(c, "签名校验失败")
|
||||
c.Abort()
|
||||
return
|
||||
@@ -118,11 +130,13 @@ func OpenAuth(cfg OpenAuthConfig) gin.HandlerFunc {
|
||||
}
|
||||
created := cfg.DB.Clauses(clause.OnConflict{DoNothing: true}).Create(&nonceRow)
|
||||
if created.Error != nil {
|
||||
openlog.Warn(c, "open_auth nonce_db_fail app_key=%s err=%v", openlog.MaskKey(appKey), created.Error)
|
||||
response.ServerError(c, "记录请求 nonce 失败")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if created.RowsAffected == 0 {
|
||||
openlog.Warn(c, "open_auth nonce_replay app_key=%s nonce=%s", openlog.MaskKey(appKey), nonce)
|
||||
response.Unauthorized(c, "重复的 X-Nonce(请勿重放请求)")
|
||||
c.Abort()
|
||||
return
|
||||
@@ -133,6 +147,9 @@ func OpenAuth(cfg OpenAuthConfig) gin.HandlerFunc {
|
||||
c.Set(CtxMerchantID, client.MerchantID)
|
||||
c.Set(openlog.CtxAPIKey, appKey)
|
||||
_ = cfg.DB.Model(&model.APIClient{}).Where("id = ?", client.ID).Update("last_used_at", now).Error
|
||||
|
||||
openlog.Info(c, "open_auth ok app_key=%s merchant_id=%d method=%s path=%s body_size=%d",
|
||||
openlog.MaskKey(appKey), client.MerchantID, method, path, len(bodyBytes))
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ func SourceOpenAuth(cfg SourceOpenAuthConfig) gin.HandlerFunc {
|
||||
c.Header("X-Request-Id", reqID)
|
||||
|
||||
if cfg.APIKey == "" || cfg.APISecret == "" {
|
||||
openlog.Warn(c, "source_open_auth uninitialized")
|
||||
response.ServerError(c, "服务端未配置 OPEN_API_KEY / OPEN_API_SECRET")
|
||||
c.Abort()
|
||||
return
|
||||
@@ -69,22 +70,26 @@ func SourceOpenAuth(cfg SourceOpenAuthConfig) gin.HandlerFunc {
|
||||
nonce := c.GetHeader("X-Nonce")
|
||||
sign := c.GetHeader("X-Sign")
|
||||
if apiKey == "" || timestamp == "" || nonce == "" || sign == "" {
|
||||
openlog.Warn(c, "source_open_auth missing_headers")
|
||||
response.Unauthorized(c, "缺少鉴权头:需要 X-Api-Key、X-Timestamp、X-Nonce、X-Sign")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if apiKey != cfg.APIKey {
|
||||
openlog.Warn(c, "source_open_auth invalid_key api_key=%s", openlog.MaskKey(apiKey))
|
||||
response.Unauthorized(c, "无效的 API Key")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if len(nonce) < 8 || len(nonce) > 64 {
|
||||
openlog.Warn(c, "source_open_auth bad_nonce_len len=%d", len(nonce))
|
||||
response.Unauthorized(c, "X-Nonce 长度需在 8~64 之间")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
ts, err := strconv.ParseInt(timestamp, 10, 64)
|
||||
if err != nil || abs64(time.Now().Unix()-ts) > cfg.SkewSeconds {
|
||||
openlog.Warn(c, "source_open_auth expired ts=%s skew=%d", timestamp, cfg.SkewSeconds)
|
||||
response.Unauthorized(c, "请求已过期或 X-Timestamp 格式错误")
|
||||
c.Abort()
|
||||
return
|
||||
@@ -92,6 +97,7 @@ func SourceOpenAuth(cfg SourceOpenAuthConfig) gin.HandlerFunc {
|
||||
|
||||
bodyBytes, err := io.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
openlog.Warn(c, "source_open_auth read_body_fail err=%v", err)
|
||||
response.BadRequest(c, "读取请求体失败")
|
||||
c.Abort()
|
||||
return
|
||||
@@ -99,16 +105,23 @@ func SourceOpenAuth(cfg SourceOpenAuthConfig) gin.HandlerFunc {
|
||||
c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
|
||||
if store.seen(apiKey+":"+nonce, time.Now().Unix(), cfg.SkewSeconds) {
|
||||
openlog.Warn(c, "source_open_auth nonce_replay nonce=%s", nonce)
|
||||
response.Unauthorized(c, "重复的 X-Nonce(请勿重放请求)")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
expected := BuildOpenSign(apiKey, cfg.APISecret, timestamp, nonce, c.Request.Method, c.Request.URL.Path, string(bodyBytes))
|
||||
if !hmac.Equal([]byte(strings.ToLower(sign)), []byte(expected)) {
|
||||
openlog.Warn(c, "source_open_auth sign_mismatch method=%s path=%s body=%s sign=%s expected=%s",
|
||||
c.Request.Method, c.Request.URL.Path, openlog.Truncate(string(bodyBytes), 200),
|
||||
openlog.MaskSign(sign), openlog.MaskSign(expected))
|
||||
response.Unauthorized(c, "签名校验失败")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
openlog.Info(c, "source_open_auth ok method=%s path=%s body_size=%d",
|
||||
c.Request.Method, c.Request.URL.Path, len(bodyBytes))
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user