TapTap OAuth 接口
概述
TapTap OpenAPI 采用统一的 Mac Token 头部签算来传递用户授权信息。
开发者接入 SDK 登录模块,在用户授权你的应用程序后,将会生成访问令牌(Access Token)。这个访问令牌在加密后会生成 Mac Token 字符串。Access Token 长期有效,只有在用户更新其账户安全信息或解除对当前应用的授权时才会失效。开发人员应妥善管理 Access Token 在其服务器上,用作与 TapTap 服务器进行后续通讯的标识。
Mac Token 生成算法见文档中的 MAC Token 算法 部分。
以下接口均为国内示例。当移动端初始化为海外时,登录即为海外,以下服务端文档流程不变,将示例中的请求域名 open.tapapis.cn
更换为海外域名 open.tapapis.com
即可。
流程
移动端用 SDK 的 TapTap 登录,可以 获取 AccessToken,里面包含:
public String kid;
public String token_type;
public String mac_key;
public String mac_algorithm;
public Set<String> scopeSet;再把移动端获取的参数发到游戏服务器,服务端签算 mac token。
请求
https://open.tapapis.cn/account/profile/v1
, header 携带mac token
。
API
当 SDK 只请求 basic_info 的权限时,请使用基础信息接口,请求 public_profile 时,请使用详细信息接口。
获取当前账户基础信息
GET https://open.tapapis.cn/account/basic-info/v1?client_id=xxx
Authorization mac token
请求参数
字段 | 类型 | 说明 |
---|---|---|
client_id | string | 该应用的 Client ID ,应与约定相同 |
响应参数
字段 | 类型 | 说明 |
---|---|---|
openid | string | 授权用户唯一标识,每个玩家在每个游戏中的 openid 都是不一样的,同一游戏获取同一玩家的 openid 总是相同 |
unionid | string | 授权用户唯一标识,一个玩家在一个厂商的所有游戏中 unionid 都是一样的,不同厂商 unionid 不同 |
请求示例
替换其中的 MAC id
和 Client ID
为自己签算的 mac token 和控制台的 Client ID
。
curl -s -H 'Authorization:MAC id="1/hC0vtMo7ke0Hkd-iI8-zcAwy7vKds9si93l7qBmNFxJkylWEOYEzGqa7k_9iw_bb3vizf-3CHc6U8hs-5a74bMFzkkz7qC2HdifBEHsW9wxOBn4OsF9vz4Cc6CWijkomnOHdwt8Km6TywOX5cxyQv0fnQQ9fEHbptkIJa
gCd33eBXg76grKmKsIR-YUZd1oVHu0aZ6BR7tpYYsCLl-LM6ilf8LZpahxQ28n2c-y33d-20YRY5NW1SnR7BorFbd00ZP97N9kwDncoM1GvSZ7n90_0ZWj4a12x1rfAWLuKEimw1oMGl574L0wE5mGoshPa-CYASaQmBDo3Q69XbjTs
KQ",ts="1618221750",nonce="adssd",mac="XWTPmq6A6LzgK8BbNDwj+kE4gzs="' "https://open.tapapis.cn/account/basic-info/v1?client_id=<Client ID>"
获取当前账户详细信息
GET https://open.tapapis.cn/account/profile/v1?client_id=xxx
Authorization mac token
请求参数
字段 | 类型 | 说明 |
---|---|---|
client_id | string | 该应用的 Client ID ,应与约定相同 |
响应参数
字段 | 类型 | 说明 |
---|---|---|
name | string | 用户名 |
avatar | string | 用户头像图片地址 |
openid | string | 授权用户唯一标识,每个玩家在每个游戏中的 openid 都是不一样的,同一游戏获取同一玩家的 openid 总是相同 |
unionid | string | 授权用户唯一标识,一个玩家在一个厂商的所有游戏中 unionid 都是一样的,不同厂商 unionid 不同 |
请求示例
替换其中的 MAC id
和 Client ID
为自己签算的 mac token 和控制台的 Client ID
。
curl -s -H 'Authorization:MAC id="1/hC0vtMo7ke0Hkd-iI8-zcAwy7vKds9si93l7qBmNFxJkylWEOYEzGqa7k_9iw_bb3vizf-3CHc6U8hs-5a74bMFzkkz7qC2HdifBEHsW9wxOBn4OsF9vz4Cc6CWijkomnOHdwt8Km6TywOX5cxyQv0fnQQ9fEHbptkIJa
gCd33eBXg76grKmKsIR-YUZd1oVHu0aZ6BR7tpYYsCLl-LM6ilf8LZpahxQ28n2c-y33d-20YRY5NW1SnR7BorFbd00ZP97N9kwDncoM1GvSZ7n90_0ZWj4a12x1rfAWLuKEimw1oMGl574L0wE5mGoshPa-CYASaQmBDo3Q69XbjTs
KQ",ts="1618221750",nonce="adssd",mac="XWTPmq6A6LzgK8BbNDwj+kE4gzs="' "https://open.tapapis.cn/account/profile/v1?client_id=<Client ID>"
其他
MAC Token 算法
MAC Token 包含以下字段:
字段 | 类型 | 说明 |
---|---|---|
kid | string | mac_key id, The key identifier. |
token_type | string | Token 类型,如 mac |
mac_key | string | mac 密钥 |
mac_algorithm | string | mac 计算的算法名称 hmac-sha-1 |
使用 Mac Token 签算一个接口:
Node.js 请求示例
const http = require('http');
const https = require('https');
const crypto = require('crypto');
function getAuthorization(requestUrl, method, keyId, macKey) {
const url = new URL(requestUrl);
const time = Math.floor(Date.now() / 1000).toString().padStart(10, '0');
const randomStr = getRandomString(16);
const host = url.hostname;
const uri = url.pathname + url.search;
const port = url.port || (url.protocol === 'https:' ? '443' : '80');
const other = '';
const sign = signData(mergeData(time, randomStr, method, uri, host, port, other), macKey);
return `MAC id="${keyId}", ts="${time}", nonce="${randomStr}", mac="${sign}"`;
}
function getRandomString(length) {
return crypto.randomBytes(length).toString('base64');
}
function mergeData(time, randomCode, httpType, uri, domain, port, other) {
let prefix =
`${time}\n${randomCode}\n${httpType}\n${uri}\n${domain}\n${port}\n`;
if (!other) {
prefix += '\n';
} else {
prefix += `${other}\n`;
}
return prefix;
}
function signData(signatureBaseString, key) {
const hmac = crypto.createHmac('sha1', key);
hmac.update(signatureBaseString);
return hmac.digest('base64');
}
const client_id = "hskc**********kklm";
const keyId = "1/VLDoiGUhNCIpUq827L**************zAJ-i8hT_w9vuPtPgdaPkWDv6K4eVe_yZnKz************EYep-T4ki5w3kyYACVnM61JJqDEKfpNnHoTZU********************iUArkgPsWEwOpZGxva7FnqbTwmpLT0a28UtiR5gyr4XXutbnE5tb4A-iSqRpqqtgABXBZd34U5Th3iJ1C666iYQFvuQL9uC-Zv7-xKCNjyPonBqU4ZWZnKLFf2mzprU5vJCA8q5by1SZxY63kZBQieHYxFjyOCQdJ-25gDlxiqDbNq08kmSdY6TB1qtQ68V37L6a8nIzyVHooX9uc2Yw";
const macKey = 'VPDalRmxtBqi******************tH937GNKIvj3';
const requestUrl = 'https://open.tapapis.cn/account/profile/v1?client_id='+ client_id ;
const method = 'GET';
const authorization = getAuthorization(requestUrl, method, keyId, macKey);
console.log(authorization);
const options = new URL(requestUrl);
const client = options.protocol === 'https:' ? https : http;
const req = client.request({
hostname: options.hostname,
port: options.port,
path: options.pathname + options.search,
method: 'GET',
headers: {
'Authorization': authorization
}
}, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log(data);
});
});
req.end();
Java 请求示例
package com.taptap;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class Authorization {
public static void main(String[] args) throws IOException {
String client_id = "0RiAlMny7jiz086FaU";
String kid = "1/hC0vtMo7ke0Hkd-iI8-zcAwy7vKds9si93l7qBmNFxJkylWEOYEzGqa7k_9iw_bb3vizf-3CHc6U8hs-5a74bMFzkkz7qC2HdifBEHsW9wxOBn4OsF9vz4Cc6CWijkomnOHdwt8Km6TywOX5cxyQv0fnQQ9fEHbptkIJagCd33eBXg76grKmKsIR-YUZd1oVHu0aZ6BR7tpYYsCLl-LM6ilf8LZpahxQ28n2c-y33d-20YRY5NW1SnR7BorFbd00ZP97N9kwDncoM1GvSZ7n90_0ZWj4a12x1rfAWLuKEimw1oMGl574L0wE5mGoshPa-CYASaQmBDo3Q69XbjTsKQ"; // kid
String mac_key = "mSUQNYUGRBPXyRyW"; // mac_key
String method = "GET";
String request_url = "https://open.tapapis.cn/account/profile/v1?client_id=" + client_id; //
String authorization = getAuthorization(request_url, method, kid, mac_key);
System.out.println(authorization);
URL url = new URL(request_url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// Http
conn.setRequestProperty("Authorization", authorization);
conn.setRequestMethod("GET");
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
StringBuilder result = new StringBuilder();
while ((line = rd.readLine()) != null) {
result.append(line);
}
rd.close();
System.out.println(result.toString());
}
/**
* @param request_url
* @param method "GET" or "POST"
* @param key_id key id by OAuth 2.0
* @param mac_key mac key by OAuth 2.0
* @return authorization string
*/
public static String getAuthorization(String request_url, String method, String key_id, String
mac_key) {
try {
URL url = new URL(request_url);
String time = String.format(Locale.US, "%010d", System.currentTimeMillis() / 1000);
String randomStr = getRandomString(16);
String host = url.getHost();
String uri = request_url.substring(request_url.lastIndexOf(host) + host.length());
String port = "80";
if (request_url.startsWith("https")) {
port = "443";
}
String other = "";
String sign = sign(mergeSign(time, randomStr, method, uri, host, port, other), mac_key);
return "MAC " + getAuthorizationParam("id", key_id) + "," + getAuthorizationParam("ts", time)
+ "," + getAuthorizationParam("nonce", randomStr) + "," + getAuthorizationParam("mac",
sign);
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
}
private static String getRandomString(int length) {
byte[] bytes = new byte[length];
new SecureRandom().nextBytes(bytes);
String base64String = Base64.getEncoder().encodeToString(bytes);
return base64String;
}
private static String mergeSign(String time, String randomCode, String httpType, String uri,
String domain, String port, String other) {
if (time.isEmpty() || randomCode.isEmpty() || httpType.isEmpty() || domain.isEmpty() || port.isEmpty())
{
return null;
}
String prefix =
time + "\n" + randomCode + "\n" + httpType + "\n" + uri + "\n" + domain + "\n" + port
+ "\n";
if (other.isEmpty()) {
prefix += "\n";
} else {
prefix += (other + "\n");
}
return prefix;
}
private static String sign(String signatureBaseString, String key) {
try {
SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
byte[] text = signatureBaseString.getBytes(StandardCharsets.UTF_8);
byte[] signatureBytes = mac.doFinal(text);
signatureBytes = Base64.getEncoder().encode(signatureBytes);
return new String(signatureBytes, StandardCharsets.UTF_8);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new IllegalStateException(e);
}
}
private static String getAuthorizationParam(String key, String value) {
if (key.isEmpty() || value.isEmpty()) {
return null;
}
return key + "=" + "\"" + value + "\"";
}
}
PHP 请求示例
<?php
/**
* 以下是配置项:请按实际情况替换参数后进行授权请求。
*/
$client_id = "请替换为控制台的 Client ID";
$kid = "请替换为客户端 SDK 授权成功后,MAC Token 中 kid 的值";
$mac_key = "请替换为客户端 SDK 授权成功后,MAC Token 中 mac_key 的值";
/**
* 获取当前账户详细信息的接口。
* 如果需要获取账户基础信息,请替换为以下 URL:
* https://open.tapapis.cn/account/basic-info/v1
*/
$url = "https://open.tapapis.cn/account/profile/v1";
/**
* 主程序逻辑
*/
// 步骤 1:设置 $method、$request_url
$method = "GET";
$request_url = $url . "?client_id=" . urlencode($client_id);
// 步骤 2:生成时间戳和随机数
$ts = time(); // 秒级当前时间戳
$nonce = randomString(5); // 随机数,至少5位
// 步骤 3:创建待签名字符串并生成签名
$signing_string = createSigningString($request_url, $ts, $nonce, $method);
$mac = sign($signing_string, $mac_key);
// 步骤 4:生成 Authorization 头部信息
$auth = sprintf('MAC id="%s",ts="%s",nonce="%s",mac="%s"', $kid, $ts, $nonce, $mac);
echo "Authorization: " . $auth . PHP_EOL . PHP_EOL;
// 步骤 5:执行 HTTP 请求并输出结果
$headers = array("Authorization: " . $auth);
$response = executeCurlRequest($request_url, $headers, $method);
echo "HTTP Status Code: " . $response['http_code'] . PHP_EOL . PHP_EOL;
if (isset($response['error'])) {
echo "Error: " . $response['error'] . PHP_EOL . PHP_EOL;
}
if (isset($response['body'])) {
echo "Response Body: " . PHP_EOL . json_encode($response['body'], JSON_PRETTY_PRINT) . PHP_EOL;
}
/**
* 创建待签名字符串
*
* @param string $request_url 请求的 URL
* @param int $ts 时间戳
* @param string $nonce 随机数
* @param string $method HTTP 方法
* @return string 待签名字符串
*/
function createSigningString($request_url, $ts, $nonce, $method)
{
$parsed_url = parse_url($request_url);
$uri = $parsed_url['path'] . '?' . $parsed_url['query'];
$domain = $parsed_url['host'];
$port = 443; // 使用 HTTPS 固定端口
return implode("\n", [
$ts,
$nonce,
$method,
$uri,
$domain,
$port,
""
]) . "\n";
}
/**
* 生成签名值
*
* @param string $signing_string 待签名字符串
* @param string $mac_key MAC 密钥
* @return string 生成的签名值
* @example sign('abc', 'def') -> dYTuFEkwcs2NmuhQ4P8JBTgjD4w=
*/
function sign($signing_string, $mac_key)
{
return base64_encode(hash_hmac('sha1', $signing_string, $mac_key, true));
}
/**
* 执行 cURL 请求并返回结果
*
* @param string $url 请求的 URL
* @param array $headers 请求头
* @param string $method HTTP 方法
* @return array 包含状态码和响应内容或错误信息
*/
function executeCurlRequest($url, $headers, $method)
{
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
// 执行请求并捕获响应
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
// 检查是否发生 cURL 错误
if (curl_errno($curl)) {
$error_message = curl_error($curl);
curl_close($curl);
return array(
'http_code' => 0,
'error' => "cURL 错误: " . $error_message,
'body' => json_decode($response, true)
);
}
// 关闭 cURL 句柄
curl_close($curl);
// 解析响应
$parsed_body = json_decode($response, true);
// 根据 HTTP 状态码返回结果
if ($httpCode >= 200 && $httpCode < 300) {
return array(
'http_code' => $httpCode,
'body' => $parsed_body
);
} else {
return array(
'http_code' => $httpCode,
'error' => "HTTP 请求失败,状态码: $httpCode",
'body' => $parsed_body
);
}
}
/**
* 生成随机字符串
*
* @param int $length 随机字符串的长度
* @return string 随机生成的字符串
*/
function randomString($length = 5)
{
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomIndex = mt_rand(0, $charactersLength - 1);
$randomString .= $characters[$randomIndex];
}
return $randomString;
}
Python3 请求示例
import base64
import hmac
import random
import string
import time
from hashlib import sha1
def get_mac_token_signature(host, request_url, method, mac_key, kid):
mac_token_pattern = 'MAC id="{kid}",ts="{ts}",nonce="{nonce}",mac="{mac}"'
timestamp = str(int(time.time()))
nonce = ''.join(random.choices(string.ascii_lowercase + string.ascii_uppercase, k=16))
sign_array = [timestamp, nonce, method, request_url, host, '443', '']
seperator = '\n'
sign_input = seperator.join(sign_array) + seperator
hmac_code = hmac.new(mac_key.encode('UTF-8'), sign_input.encode('UTF-8'), sha1)
mac_str = base64.b64encode(hmac_code.digest()).decode('UTF-8')
return mac_token_pattern.format(kid=kid, ts=timestamp, nonce=nonce,
mac=mac_str)
if __name__ == '__main__':
kid = "1/hC0vtMo7ke0Hkd-iI8-zcAwy7vKds9si93l7qBmNFxJkylWEOYEzGqa7k_9iw_bb3vizf-3CHc6U8hs-5a74bMFzkkz7qC2HdifBEHsW9wxOBn4OsF9vz4Cc6CWijkomnOHdwt8Km6TywOX5cxyQv0fnQQ9fEHbptkIJagCd33eBXg76grKmKsIR-YUZd1oVHu0aZ6BR7tpYYsCLl-LM6ilf8LZpahxQ28n2c-y33d-20YRY5NW1SnR7BorFbd00ZP97N9kwDncoM1GvSZ7n90_0ZWj4a12x1rfAWLuKEimw1oMGl574L0wE5mGoshPa-CYASaQmBDo3Q69XbjTsKQ"
mac_key = "mSUQNYUGRBPXyRyW"
client_id = "0RiAlMny7jiz086FaU"
signature = get_mac_token_signature('open.tapapis.cn', '/account/profile/v1?client_id=' + client_id,
'GET', mac_key, kid)
print(signature)
Go 请求示例
package main
import (
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"fmt"
"io"
"net/http"
"strconv"
"time"
)
func main() {
// 替换 clientId、accessToken、macKey 参数
// clientId 参数在 TapDC 后台查看
clientId := "请替换为控制台的 `Client ID`"
// TapTap 登录成功后 TapSDK 返回的 kid
kid := "1/jvsVxFC6-PIUiXvZVVtv1hogX5q9Z1y_rp-AtVjE3iyHikXXfd_2h-i0wLmc9UjLJwhH6fQ8cvGrklONdvy2J5YfoqzV0ewGPMSLkQIkRv_xaLaYPariWbrkP1MtG2b4CzR1KHvuSCJHewCmTFZmsyNGojTJr5t75f5Nc8j-jjCYeDtFO0-XFI_J7kzktswzzsmISt7cx49QVess-VbaQcU31pEDb_OA03I28H5ehIvqQ0CQdf1LieLyONcH97l1IEU39AirioF_KGJccVG64QsgWmzxLPwmfTurw4cwBPo04yuXnas4YI5haE2UxtckNCpagP19drtGW57-HaAdww"
// TapTap 登录成功后 TapSDK 返回的 mac_key
macKey := "fTCuDUDDmNny7a36EWbhUDLaqpoDMQu2hCi9qAJ5"
// 随机数,正式上线请替换
nonce := "8IBTHwOdqNKAWeKl7plt66=="
// 时间戳转换成字符串
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
// 请求 url 相关
reqHost := "open.tapapis.cn"
reqURI := "/account/profile/v1?client_id=" + clientId
reqURL := "https://" + reqHost + reqURI
macStr := timestamp + "\n" + nonce + "\n" + "GET" + "\n" + reqURI + "\n" + reqHost + "\n" + "443" + "\n\n"
mac := hmacSha1(macStr, macKey)
authorization := "MAC id=" + "\"" + kid + "\"" + "," + "ts=" + "\"" + timestamp + "\"" + "," + "nonce=" + "\"" + nonce + "\"" + "," + "mac=" + "\"" + mac + "\""
client := http.Client{}
req, err := http.NewRequest(http.MethodGet, reqURL, nil)
if err != nil {
fmt.Println(err.Error())
return
}
// 添加请求头
req.Header.Add("Authorization", authorization)
// 发送请求
resp, err := client.Do(req)
if err != nil {
fmt.Println(err.Error())
return
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(string(respBody))
}
/*
HMAC-SHA1 签名
*/
func hmacSha1(valStr, keyStr string) string {
key := []byte(keyStr)
mac := hmac.New(sha1.New, key)
mac.Write([]byte(valStr))
// 进行 Base64 编码
return base64.StdEncoding.EncodeToString(mac.Sum(nil))
}
C# 请求示例
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text;
public class TapLoginOAuth : MonoBehaviour
{
private string host = "open.tapapis.cn";
private string clientId = "应用的 Client id";
private string macKey = "客户端登录返回的 macKey 值";
private string kid = "客户端登录返回的 kid 值";
// Start is called before the first frame update
void Start()
{
// 发起网络请求
StartCoroutine(SendRequest());
}
// Update is called once per frame
void Update()
{
}
IEnumerator SendRequest()
{
// 构建请求 URL
string requestUrl = $"/account/profile/v1?client_id={clientId}";
// 生成签名
string signature = GetMacTokenSignature(host, requestUrl, "GET", macKey, kid);
Debug.Log("Generated Signature: " + signature);
// 发送 GET 请求到服务器
using (var httpClient = new HttpClient())
{
var uri = new Uri($"https://{host}{requestUrl}");
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("Authorization", signature);
// 发送请求并等待响应
var response = httpClient.SendAsync(request).Result;
var responseBody = response.Content.ReadAsStringAsync().Result;
// 输出服务器响应
Debug.Log("Server Response: " + responseBody);
}
yield return null;
}
string GetMacTokenSignature(string host, string requestUrl, string method, string macKey, string kid)
{
string macTokenPattern = "MAC id=\"{0}\",ts=\"{1}\",nonce=\"{2}\",mac=\"{3}\"";
string timestamp = ((int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds).ToString();
string nonce = GenerateNonce(16);
string[] signArray = { timestamp, nonce, method, requestUrl, host, "443", "" };
string separator = "\n";
string signInput = string.Join(separator, signArray) + separator;
using (var hmac = new System.Security.Cryptography.HMACSHA1(Encoding.UTF8.GetBytes(macKey)))
{
byte[] hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(signInput));
string macStr = Convert.ToBase64String(hash);
return string.Format(macTokenPattern, kid, timestamp, nonce, macStr);
}
}
string GenerateNonce(int length)
{
const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
var random = new System.Random();
var nonce = new char[length];
for (int i = 0; i < length; i++)
{
nonce[i] = chars[random.Next(chars.Length)];
}
return new string(nonce);
}
}
脚本请求示例
可用此脚本验证直接替换参数,用来验证自己服务端签算的 mac token 是否正确。
CLIENT_ID 替换为控制台获取的 Client ID
,KID 和 MAC_KEY 为客户端登录成功后的 kid
、mac_key
:
#!/usr/bin/env bash
# 客户端 ID
CLIENT_ID="请替换为控制台的 `Client ID`"
# SDK 获取的 kid
KID="1/hC0vtMo7ke0Hkd-iI8-zcAwy7vKds9si93l7qBmNFxJkylWEOYEzGqa7k_9iw_bb3vizf-3CHc6U8hs-5a74bMFzkkz7qC2HdifBEHsW9wxOBn4OsF9vz4Cc6CWijkomnOHdwt8Km6TywOX5cxyQv0fnQQ9fEHbptkIJagCd33eBXg76grKmKsIR-YUZd1oVHu0aZ6BR7tpYYsCLl-LM6ilf8LZpahxQ28n2c-y33d-20YRY5NW1SnR7BorFbd00ZP97N9kwDncoM1GvSZ7n90_0ZWj4a12x1rfAWLuKEimw1oMGl574L0wE5mGoshPa-CYASaQmBDo3Q69XbjTsKQ"
# SDK 获取的 mac_key
MAC_KEY="mSUQNYUGRBPXyRyW"
# 随机数,正式上线请替换
NONCE="8IBTHwOdqNKAWeKl7plt8g=="
# 当前时间戳
TS=$(date +%s)
# 请求方法
METHOD="GET"
# 请求地址 (带 query string)
REQUEST_URI="/account/profile/v1?client_id=${CLIENT_ID}"
# 请求域名
REQUEST_HOST="open.tapapis.cn"
MAC=$(printf "%s\n%s\n%s\n%s\n%s\n443\n\n" "${TS}" "${NONCE}" "${METHOD}" "${REQUEST_URI}" "${REQUEST_HOST}" | openssl dgst -binary -sha1 -hmac ${MAC_KEY} | base64)
AUTHORIZATION=$(printf 'MAC id="%s",ts="%s",nonce="%s",mac="%s"' "${KID}" "${TS}" "${NONCE}" "${MAC}")
curl -s -H"Authorization:${AUTHORIZATION}" "https://${REQUEST_HOST}${REQUEST_URI}"
通用接口错误信息
统一格式
字段 | 类型 | 说明 |
---|---|---|
code | int | 预留字段,用于以后追踪问题 |
error | string | 错误码,代码逻辑判断时使用 |
error_description | string | 错误描述信息,开发的时候用来帮助理解和解决发生的错误 |
错误响应
错误码 | 详细描述 |
---|---|
invalid_request | 请求缺少某个必需参数,包含一个不支持的参数或参数值,或者格式不正确 |
invalid_time | MAC Token 算法中,ts 时间不合法,应请求服务器时间重新构造 |
invalid_client | client_id 参数无效 |
access_denied | 授权服务器拒绝请求 这个状态出现在拿着 token 请求用户资源时,如出现,客户端应退出本地的用户登录信息,引导用户重新登录 |
forbidden | 用户没有对当前动作的权限,引导重新身份验证并不能提供任何帮助,而且这个请求也不应该被重复提交 |
not_found | 请求失败,请求所希望得到的资源未被在服务器上发现。在参数相同的情况下,不应该重复请求 |
server_error | 服务器出现异常情况 可稍等后重新尝试请求,但需有尝试上限,建议最多 3 次,如一直失败,则中断并告知用户 |
insufficient_scope | 移动端进行 TapTap 授权使用的授权范围与服务端调用的 OAuth 接口不匹配导致,例如:移动端授权采用 basic_info 权限而服务端调用 获取当前账户基础信息 API 时则会返回该异常 |