188 lines
5.6 KiB
Go
188 lines
5.6 KiB
Go
package main
|
||
|
||
import (
|
||
"bytes"
|
||
"crypto/aes"
|
||
"crypto/cipher"
|
||
"encoding/base64"
|
||
"encoding/json"
|
||
"fmt"
|
||
"io/ioutil"
|
||
"net/http"
|
||
|
||
credential "github.com/bytedance/douyin-openapi-credential-go/client"
|
||
openApiSdkClient "github.com/bytedance/douyin-openapi-sdk-go/client"
|
||
)
|
||
|
||
// 获取 access_token 的函数
|
||
func GetAccessToken() (string, error) {
|
||
// 初始化 SDK 客户端,设置 app_id 和 secret
|
||
opt := new(credential.Config).
|
||
SetClientKey("tt8b32fd8f14071db707"). // 替换为你的 app_id
|
||
SetClientSecret("44018e80b1bde34395a52de67ce1e0c37c572d80") // 替换为你的 secret
|
||
|
||
// 创建 SDK 客户端
|
||
sdkClient, err := openApiSdkClient.NewClient(opt)
|
||
if err != nil {
|
||
return "", fmt.Errorf("SDK 初始化失败: %v", err)
|
||
}
|
||
|
||
// 构造获取 token 的请求参数
|
||
sdkRequest := &openApiSdkClient.AppsV2TokenRequest{}
|
||
sdkRequest.SetAppid("tt8b32fd8f14071db707") // 设置应用 ID
|
||
sdkRequest.SetGrantType("client_credential") // 设置授权类型(client_credentials 模式)
|
||
sdkRequest.SetSecret("44018e80b1bde34395a52de67ce1e0c37c572d80") // 设置密钥
|
||
|
||
// 调用 SDK 获取 access_token
|
||
sdkResponse, err := sdkClient.AppsV2Token(sdkRequest)
|
||
if err != nil {
|
||
fmt.Println("sdk call err:", err)
|
||
return "", nil
|
||
}
|
||
fmt.Println("sdk数据", sdkResponse.Data)
|
||
|
||
// 返回 access_token
|
||
return *sdkResponse.Data.AccessToken, nil
|
||
}
|
||
func DecryptAES128CBC(encryptedData, sessionKey, iv string) ([]byte, error) {
|
||
// Base64 解码 key 和 iv
|
||
aesKey, err := base64.StdEncoding.DecodeString(sessionKey)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("sessionKey Base64 解码失败: %v", err)
|
||
}
|
||
|
||
initialVector, err := base64.StdEncoding.DecodeString(iv)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("iv Base64 解码失败: %v", err)
|
||
}
|
||
|
||
// 检查 key 和 iv 长度是否为 16 字节
|
||
if len(aesKey) != 16 {
|
||
return nil, fmt.Errorf("AES key 长度必须为 16 字节")
|
||
}
|
||
if len(initialVector) != 16 {
|
||
return nil, fmt.Errorf("IV 长度必须为 16 字节")
|
||
}
|
||
|
||
// Base64 解码密文
|
||
ciphertext, err := base64.StdEncoding.DecodeString(encryptedData)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("encryptedData Base64 解码失败: %v", err)
|
||
}
|
||
|
||
// 创建 AES 块
|
||
block, err := aes.NewCipher(aesKey)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("创建 AES Cipher 失败: %v", err)
|
||
}
|
||
|
||
// 检查密文长度是否为块大小的倍数
|
||
if len(ciphertext)%aes.BlockSize != 0 {
|
||
return nil, fmt.Errorf("密文长度必须是 AES 块大小的倍数")
|
||
}
|
||
|
||
// 创建 CBC 解密器
|
||
mode := cipher.NewCBCDecrypter(block, initialVector)
|
||
|
||
// 解密数据
|
||
decrypted := make([]byte, len(ciphertext))
|
||
mode.CryptBlocks(decrypted, ciphertext)
|
||
|
||
// 去除 PKCS#7 填充
|
||
decrypted, err = PKCS7Unpad(decrypted)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("去除 PKCS#7 填充失败: %v", err)
|
||
}
|
||
|
||
return decrypted, nil
|
||
}
|
||
|
||
// PKCS#7 填充去除函数
|
||
func PKCS7Unpad(data []byte) ([]byte, error) {
|
||
length := len(data)
|
||
if length == 0 {
|
||
return nil, fmt.Errorf("解密后数据长度为 0")
|
||
}
|
||
padding := int(data[length-1])
|
||
if padding > length || padding == 0 {
|
||
return nil, fmt.Errorf("无效的填充数据")
|
||
}
|
||
return data[:length-padding], nil
|
||
}
|
||
|
||
func main() {
|
||
// 定义 HTTP 路由和处理函数
|
||
http.HandleFunc("/get-ecpm", func(w http.ResponseWriter, r *http.Request) {
|
||
// 检查 HTTP 请求方法是否为 GET
|
||
if r.Method != http.MethodGet {
|
||
http.Error(w, "无效的请求方法", http.StatusMethodNotAllowed)
|
||
return
|
||
}
|
||
|
||
// 从请求中获取参数
|
||
openID := r.URL.Query().Get("open_id") // 获取 open_id
|
||
mpID := r.URL.Query().Get("mp_id") // 获取 mp_id
|
||
iv := r.URL.Query().Get("iv") // 获取初始向量
|
||
dateHour := r.URL.Query().Get("date_hour") // 获取 date_hour
|
||
|
||
// 检查是否有缺少参数
|
||
if openID == "" || mpID == "" || dateHour == "" {
|
||
http.Error(w, "缺少必要的参数", http.StatusBadRequest)
|
||
return
|
||
}
|
||
// 解密 encryptedData
|
||
decryptedOpenID, err := DecryptAES128CBC(openID, sessionKey, iv)
|
||
if err != nil {
|
||
http.Error(w, fmt.Sprintf("解密失败: %v", err), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
// 动态获取 access_token
|
||
accessToken, err := GetAccessToken()
|
||
if err != nil {
|
||
http.Error(w, fmt.Sprintf("获取 access_token 失败: %v", err), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
fmt.Println("动态token为", accessToken)
|
||
// 构造请求体
|
||
requestBody := map[string]string{
|
||
"open_id": openID,
|
||
"mp_id": mpID,
|
||
"date_hour": dateHour,
|
||
"access_token": accessToken,
|
||
}
|
||
requestBodyJSON, err := json.Marshal(requestBody) // 转为 JSON 格式
|
||
if err != nil {
|
||
http.Error(w, "请求体编码失败", http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// 向抖音 API 发起请求
|
||
apiURL := "https://minigame.zijieapi.com/mgplatform/api/apps/data/get_ecpm" // 抖音 API 地址
|
||
resp, err := http.Post(apiURL, "application/json", bytes.NewBuffer(requestBodyJSON))
|
||
if err != nil {
|
||
http.Error(w, "调用抖音 API 失败", http.StatusInternalServerError)
|
||
return
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
// 读取抖音 API 的响应
|
||
responseBody, err := ioutil.ReadAll(resp.Body)
|
||
if err != nil {
|
||
http.Error(w, "读取抖音 API 响应失败", http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// 将抖音 API 的响应返回给客户端
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.WriteHeader(resp.StatusCode)
|
||
w.Write(responseBody)
|
||
})
|
||
|
||
// 启动 HTTP 服务器
|
||
fmt.Println("服务器已启动:http://localhost:8080")
|
||
err := http.ListenAndServe(":8080", nil) // 监听 8080 端口
|
||
if err != nil {
|
||
fmt.Println("服务器启动失败:", err)
|
||
}
|
||
}
|