开发人员控制台

请求访问令牌


请求访问令牌

您必须先在服务器侧执行以下操作,才能使用Amazon Device Messaging(ADM)将消息发送到应用实例:

  • 获取并存储应用实例的注册ID;有关更多详细信息,请参阅集成您的应用
  • 交换用于获取当前访问令牌的OAuth客户端凭证。本文将介绍这一过程。

要发送消息以及了解有关ADM高层架构的信息,请参阅ADM下的发送消息概览

客户端凭证和访问令牌

要获取访问令牌,您的服务器需要向ADM服务器提供OAuth客户端凭证。您的客户端凭证将由亚马逊分配,它们是对应用唯一的两组数据:client_id和client_secret值。您的服务器在其请求中使用您客户端凭证的这两组数据来获取ADM访问令牌。有关如何获取客户端凭证的信息,请参阅获取凭证

访问令牌是一种短​期元数据,它向ADM验证您服务器的身份,以便您可以发送消息。当您使用ADM发送消息时,该消息请求必须包含一个访问令牌。尽管服务器在任何给定时间只使用单个访问令牌,但您必须在旧访问令牌过期后获取一个新的令牌。此外,尽管可以在多个服务器之间共享一个访问令牌,但是通常是每个服务器获取各自的访问令牌并单独请求、跟踪以及使用它来发送消息,这样会更方便。

请求格式

您的服务器通过在对ADM服务器的请求调用中提供您的客户端凭证,获取其访问令牌。您可以使用这一相同的请求获取初始访问令牌,也可以在先前的访问令牌过期后获取一个新的令牌。

要获取访问令牌,您的服务器需要在HTTPS连接上发出一个POST请求。该请求与以下内容类似:

POST /auth/O2/token HTTP/1.1
Host: api.amazon.com
Content-Type: application/x-www-form-urlencoded;charset=UTF-8

grant_type=client_credentials&scope=messaging:push&client_id=(YOUR_CLIENT_ID)&client_secret=(YOUR_CLIENT_SECRET)

该请求本身由两部分组成:标头和消息正文。标头必须包含以下字段:

字段 描述 示例
Content-Type 资源的内容类型。必须为:application/x-www-form-urlencoded Content-Type: application/x-www-form-urlencoded

对于消息正文的内容,您必须提供URL编码字符串,其中包含以下参数:

参数 描述 示例
grant_type 值必须为client_credentials grant_type=client_credentials
scope 值必须为messaging:push scope=messaging:push
client_id 您的客户端凭证的“客户端标识符”部分。 client_id=amzn1.iba-client.b2b360f8a77d457981625636121d6edf
client_secret 您的客户端凭证的“客户端密钥”部分。 client_secret=c559965801308f2bb79ca787 b1dfc8deece8a2fd7d7618946cec1635d26dcbfb

响应格式

在成功接收并解释您的POST请求消息之后,ADM会向您的服务器发送一条类似如下的HTTP响应消息:

X-Amzn-RequestId: d917ceac-2245-11e2-a270-0bc161cb589d
Content-Type: application/json

{
  "access_token":"Atc|MQEWYJxEnP3I1ND03ZzbY_NxQkA7Kn7Aioev_OfMRcyVQ4NxGzJMEaKJ8f0lSOiV-yW270o6fnkI",
  "expires_in":3600,
  "scope":"messaging:push",
  "token_type":"Bearer"
}

响应的标头包含以下字段:

标头 描述 示例
X-Amzn-RequestId 由ADM创建的唯一标识请求的值。万一您在使用ADM时遇到问题,亚马逊可以使用此值来解决问题。 X-Amzn-RequestId: d917ceac-2245-11e2-a270-0bc161cb589d
Content-Type 资源的内容类型:application/json Content-Type: application/json

当您的某一服务器成功请求访问令牌时,您将收到200状态代码响应,并且对该请求的响应的消息正文将包含访问令牌及其使用期限(以秒为单位)。

参数 描述 示例
access_token ​必须用于所有入队请求的访问令牌。 "access_token":"Atc|MQEWYJxEnP3I1ND03Zz..."(为了易于阅读而缩短了示例)
expires_in 访问令牌使用期限的持续时间(以秒为单位)。例如,值“3600”表示访问令牌在从生成响应时算起的一个小时后过期。 "expires_in":3600
token_type ​所颁发的令牌的类型。支持的令牌类型之一。目前仅支持“持有者”令牌。 "token_type":"Bearer"
scope 访问令牌请求中指定的范围。值将为messaging:push​。 "scope":"messaging:push"

ADM对未成功的请求返回非200错误状态代码。对于非200代码,响应消息可能在JSONObject的正文中包含以下参数:

  • reason: ​不接受请求的原因。

下表列出了访问令牌请求可能的错误状态代码。

代码 描述 示例
400 此响应的原因包括:* 授权服务器不支持该内容类型。换而言之,这不是application/x-www-form-urlencoded。*请求中缺少必需的参数:client_idclient_secretscopegrant-type。*请求存在格式错误。*尚未对安全配置文件启用ADM。请参阅​获取凭证​。 INVALID_REQUEST
400 客户端无权执行请求的操作。 UNAUTHORIZED_CLIENT
400 授权服务器不支持此授予类型。换而言之,它不是client_credentials UNSUPPORTED_GRANT_TYPE
400 所请求的范围无效。换而言之,它不是messaging:push INVALID_SCOPE
401 客户端身份验证失败。 INVALID_CLIENT
500 出现内部服务器错误。请求者可以重试请求。 SERVER_ERROR
503 服务器暂时不可用。请求者必须按照响应中包含的Retry-After标头稍后重试。请参阅HTTP/1.1规范第14.37节,以了解Retry-After值的可能格式。 SERVICE_UNAVAILABLE

发出令牌请求并处理响应

以下代码示例列举了您的服务器软件发送访问令牌请求和处理ADM服务器的响应的方式:

/**
  * 要获取访问令牌,请向亚马逊发出HTTPS请求,
  * 并提供client_id和client_secret值。
  */
public String getAuthToken(String clientId, String clientSecret) throws Exception
{
     // 对包含clientID和clientSecret值的请求正文进行编码。
     String body = "grant_type="    + URLEncoder.encode("client_credentials", "UTF-8") + "&" +
                   "scope="         + URLEncoder.encode("messaging:push", "UTF-8")     + "&" +
                   "client_id="     + URLEncoder.encode(clientId, "UTF-8")             + "&" +
                   "client_secret=" + URLEncoder.encode(clientSecret, "UTF-8");

     // 利用访问令牌请求的基本URL来创建一个新的URL对象。
     URL authUrl = new URL("https://api.amazon.com/auth/O2/token");

     // 生成HTTPS连接。您无法通过HTTP建立连接。
     HttpsURLConnection con = (HttpsURLConnection) authUrl.openConnection();
     con.setDoOutput( true );
     con.setRequestMethod( "POST" );

     // 设置Content-Type标头。
     con.setRequestProperty( "Content-Type" , "application/x-www-form-urlencoded" );
     con.setRequestProperty( "Charset" , "UTF-8" );
     // 通过连接发送编码的参数。
     OutputStream os = con.getOutputStream();
     os.write(body.getBytes( "UTF-8" ));
     os.flush();
     con.connect();

     // 将响应转换为String对象。
     String responseContent = parseResponse(con.getInputStream());

     // 创建一个新的JSONObject来保存访问令牌并
     // 从响应中提取令牌。
     org.json. JSONObject parsedObject = new org.json.JSONObject(responseContent);
     String accessToken = parsedObject.getString("access_token");
     return accessToken;
}

private String parseResponse(InputStream in) throws Exception
{
     InputStreamReader inputStream = new InputStreamReader(in, "UTF-8" );
     BufferedReader buff = new BufferedReader(inputStream);

     StringBuilder sb = new StringBuilder();
     String line = buff.readLine();
     while (line != null )
     {
       sb.append(line);
       line = buff.readLine();
     }

     return sb.toString();
}