使用 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
}
记录就到这里了,有问题可以私聊一下。。。