Go+Python3实现苹果搜索广告API访问OAuth2验证

本文介绍了如何使用OAuth2授权在Apple Search Ads API中管理访问,包括邀请用户、生成密钥对、上传公钥、获取访问令牌等步骤。重点讨论了使用Python3的JWT加密生成访问令牌的过程,并展示了使用Golang请求token接口的代码片段。对于PHP和Golang开发者,文章提出了在这些语言中实现相应加密算法的挑战。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用 OAuth 2 的一个直接优势是无需或共享用户登录和密码凭据即可管理对帐户的访问,要通过验证我们需要做到以下几步:

  • 邀请将要访问 API 权限的用户,也就是用户用自己苹果手机的AppID在苹果官方注册登录一下,然后让管理员根据开发者本人的苹果手机的AppID邀请开发者。

  • 开发者本机通过OpenSSL来生成私钥-公钥对。

  • 上传公钥到官方后台得到苹果官方返回的参数。

  • 使用该参数及本地私钥请求token接口获取访问令牌。

  • 携带访问令牌访问公司业务需要使用的API接口。

如何邀请用户可以参考如下链接(有疑问可以评论或私聊):

https://searchads.apple.com/help/get-started/0011-invite-users-to-your-account

 OpenSSL的安装流程咱就不聊了哈,网上有很多教程,咱这里把生成秘钥的两个命令贴一下:

私钥:openssl ecparam -genkey -name prime256v1 -noout -out private-key.pem
公钥:openssl ec -in private-key.pem -pubout -out public-key.pem

上传公钥的流程如下:

如有疑问也可以参考官方文档:

https://developer.apple.com/documentation/apple_search_ads/implementing_oauth_for_the_apple_search_ads_api

 之后就是根据苹果返回的参数以及本地私钥来生成访问token接口的令牌了,这里咱着重聊一下,官方给的案例是用Python3的jwt加密来实现对秘钥的加密,底层加密算法是【ES256(ECDSA-SHA256)】,笔者是PHP+Golang开发者,这两种语言实现不了该加密算法,后面有实现的大佬万望教一下小弟,Python3代码如下:

import jwt
import datetime as dt

client_id = 'SEARCHADS.27478e71-3bb0-4588-998c-182e2b405577'
team_id = 'SEARCHADS.27478e71-3bb0-4588-998c-182e2b405577' 
key_id = 'bacaebda-e219-41ee-a907-e2c25b24d1b2' 
audience = 'https://appleid.apple.com'
alg = 'ES256'

# Define issue timestamp.
issued_at_timestamp = int(dt.datetime.utcnow().timestamp())
# Define expiration timestamp. May not exceed 180 days from issue timestamp.
expiration_timestamp = issued_at_timestamp + 86400*180 

# Define JWT headers.
headers = dict()
headers['alg'] = alg
headers['kid'] = key_id

# Define JWT payload.
payload = dict()
payload['sub'] = client_id
payload['aud'] = audience
payload['iat'] = issued_at_timestamp
payload['exp'] = expiration_timestamp
payload['iss'] = team_id 

# Path to signed private key.
KEY_FILE = 'private-key.pem' 

with open(KEY_FILE,'r') as key_file:
     key = ''.join(key_file.readlines())

client_secret = jwt.encode(
payload=payload,  
headers=headers,
algorithm=alg,  
key=key
)

with open('client_secret.txt', 'w') as output: 
     output.write(client_secret.decode("utf-8"))

生成完访问令牌之后就需要使用Golang来访问token接口来生成访问report接口的令牌,代码片段如下:

package utils

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

)

const GUIYIN_TOKEN_URL = "https://appleid.apple.com/auth/oauth2/token?client_secret=%s&client_id=%s&grant_type=%s&scope=%s"

type GuiYinTokenRule struct {
	AccessToken string `json:"access_token",omitempty`
	ExpiresIn   int    `json:"expires_in",omitempty`
	TokenType   string `json:"token_type",omitempty`
	Error       string `json:"error",omitempty`
}

func GetGuiYinToken() string {
	redisToken := NewRedis("REDIS_ALL_HOST1")
	if accessToken, accessTokenErr := redisToken.Get("guiyin_url_token"); accessTokenErr == nil && !common.Empty(accessToken) {
		return accessToken.(string)
	}

	client_secret := `client_secret`
	client_id := `client_id`
	grant_type := `client_credentials`
	scope := `searchadsorg`
	url := fmt.Sprintf(GUIYIN_TOKEN_URL, client_secret, client_id, grant_type, scope)
	client := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		},
	}

	reqest, reqestErr := http.NewRequest("POST", url, nil)
	reqest.Header.Add("Host", "appleid.apple.com")
	reqest.Header.Add("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
	if reqestErr != nil {
		log.WithFields(log.Fields{"reqestErr": reqestErr}).Warn("GetGuiYinToken")
		return ""
	}

	tokenBody, tokenErr := client.Do(reqest)
	if tokenErr != nil {
		log.WithFields(log.Fields{"tokenErr": tokenErr}).Warn("GetGuiYinToken")
		return ""
	}

	defer tokenBody.Body.Close()
	tokenResp := GuiYinTokenRule{}
	decoder := json.NewDecoder(tokenBody.Body)
	if decodeErr := decoder.Decode(&tokenResp); decodeErr != nil {
		log.WithFields(log.Fields{"decodeErr": decodeErr, "Body": tokenBody.Body}).Warn("GetGuiYinToken")
		return ""
	}

	if len(tokenResp.Error) > 0 {
		log.WithFields(log.Fields{"tokenResp": tokenResp}).Warn("GetGuiYinToken")
		return ""
	}

	setResult, setErr := redisToken.Set("guiyin_url_token", tokenResp.AccessToken, int64(tokenResp.ExpiresIn)-100)
	if setErr != nil || !setResult {
		log.WithFields(log.Fields{"error": setErr, "setResult": setResult}).Warn("GetGuiYinToken")
		return ""
	}

	return tokenResp.AccessToken
}

记录就到这里了,有问题可以私聊一下。。。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luyaran

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值