Initial commit

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 13:57:10 +08:00
co-authored by Claude Opus 5
commit 8679200f41
1897 changed files with 257900 additions and 0 deletions
+69
View File
@@ -0,0 +1,69 @@
package activityclient
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"strconv"
"strings"
"time"
)
// 与 activity-public-server middleware/svcauth 协议保持一致:
//
// X-Svc-AppId : 调用方 appId
// X-Svc-Ts : 请求生成的 Unix 秒(服务端校验 ±300s)
// X-Svc-Sign : HMAC_SHA256(canonical, secret) 的 hex 小写
//
// canonical 串结构("\n" 分隔):
//
// appId
// ts
// METHOD // 大写
// path // URL.Path,不含 query
// rawQuery // URL.RawQuery 原样,不重排序
// sha256_hex(body) // 空 body 也参与运算
const (
headerSvcAppId = "X-Svc-AppId"
headerSvcTs = "X-Svc-Ts"
headerSvcSign = "X-Svc-Sign"
)
// BuildSvcHeaders 生成服务间调用鉴权头。
// method/path/rawQuery 必须与服务端 c.Request 上看到的一致;
// body 传原始字节(GET 等无 body 接口传 nil)。
// appId 或 secret 为空时返回 nil,调用方按未签名处理。
func BuildSvcHeaders(appId, secret, method, path, rawQuery string, body []byte) map[string]string {
if appId == "" || secret == "" {
return nil
}
ts := strconv.FormatInt(time.Now().Unix(), 10)
return map[string]string{
headerSvcAppId: appId,
headerSvcTs: ts,
headerSvcSign: computeSvcSign(secret, appId, ts, strings.ToUpper(method), path, rawQuery, hashBody(body)),
}
}
// hashBody 计算 body 的 sha256 hexnil/空 body 也返回 sha256("") 的 hex
func hashBody(body []byte) string {
sum := sha256.Sum256(body)
return hex.EncodeToString(sum[:])
}
// computeSvcSign 按 canonical 串拼接后做 HMAC-SHA256
func computeSvcSign(secret, appId, ts, method, path, rawQuery, bodyHash string) string {
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(appId))
h.Write([]byte{'\n'})
h.Write([]byte(ts))
h.Write([]byte{'\n'})
h.Write([]byte(method))
h.Write([]byte{'\n'})
h.Write([]byte(path))
h.Write([]byte{'\n'})
h.Write([]byte(rawQuery))
h.Write([]byte{'\n'})
h.Write([]byte(bodyHash))
return hex.EncodeToString(h.Sum(nil))
}