生成 JWT 访问令牌
POST /api/auth/token
POST https://api.avista.global/api/auth/token使用 X.509 客户端证书 + OAuth 2.0 凭证(clientId/clientSecret)进行身份验证,获取有效期为 30 分钟(1800 秒)的 JWT 令牌。
请求头
| 请求头 | 类型 | 必填 | 描述 |
|---|---|---|---|
X-SSL-Client-Cert | string | 是 | URL 编码的 PEM 客户端证书(标准 NGINX 头) |
请求体
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
clientId | string | 是 | 通过凭证创建获取的 OAuth 2.0 客户端 ID |
clientSecret | string | 是 | OAuth 2.0 客户端密钥(8-64 个字符) |
{
"clientId": "account-93-550e8400",
"clientSecret": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
}Curl 示例
curl -X POST https://api.avista.global/api/auth/token \
-H "Content-Type: application/json" \
-H "X-SSL-Client-Cert: $(cat ./client-cert.pem | python3 -c 'import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read()))')" \
-d '{
"clientId": "YOUR_CLIENT_ID",
"clientSecret": "YOUR_CLIENT_SECRET"
}'代码示例
const axios = require('axios');
const fs = require('fs');
const certificate = fs.readFileSync('./client-cert.pem', 'utf8');
const encodedCert = encodeURIComponent(certificate);
const response = await axios.post('https://api.avista.global/api/auth/token', {
clientId: process.env.AVISTA_CLIENT_ID,
clientSecret: process.env.AVISTA_CLIENT_SECRET,
}, {
headers: {
'Content-Type': 'application/json',
'X-SSL-Client-Cert': encodedCert,
},
});
console.log('Token:', response.data.access_token);
console.log('Expires in:', response.data.expires_in, 'seconds');import os
import requests
import urllib.parse
with open('client-cert.pem', 'r') as f:
certificate = f.read()
encoded_cert = urllib.parse.quote(certificate)
response = requests.post(
'https://api.avista.global/api/auth/token',
json={
'clientId': os.environ['AVISTA_CLIENT_ID'],
'clientSecret': os.environ['AVISTA_CLIENT_SECRET'],
},
headers={
'Content-Type': 'application/json',
'X-SSL-Client-Cert': encoded_cert,
},
)
data = response.json()
print(f"Token: {data['access_token']}")
print(f"Expires in: {data['expires_in']} seconds")<?php
$certificate = file_get_contents('./client-cert.pem');
$encodedCert = rawurlencode($certificate);
$ch = curl_init('https://api.avista.global/api/auth/token');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-SSL-Client-Cert: ' . $encodedCert,
],
CURLOPT_POSTFIELDS => json_encode([
'clientId' => getenv('AVISTA_CLIENT_ID'),
'clientSecret' => getenv('AVISTA_CLIENT_SECRET'),
]),
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
echo "Token: " . $data['access_token'] . "\n";
echo "Expires in: " . $data['expires_in'] . " seconds\n";import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
String certificate = Files.readString(Path.of("client-cert.pem"));
// IMPORTANT: Use URLEncoder.encode + replace "+" with "%20"
// Java's URLEncoder uses application/x-www-form-urlencoded (spaces as +)
// The API expects RFC 3986 percent-encoding (spaces as %20)
String encodedCert = URLEncoder.encode(certificate, StandardCharsets.UTF_8)
.replace("+", "%20");
String body = String.format(
"{\"clientId\":\"%s\",\"clientSecret\":\"%s\"}",
System.getenv("AVISTA_CLIENT_ID"),
System.getenv("AVISTA_CLIENT_SECRET")
);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.avista.global/api/auth/token"))
.header("Content-Type", "application/json")
.header("X-SSL-Client-Cert", encodedCert)
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Response: " + response.body());常见错误 — 证书 URL 编码
证书必须使用 百分号编码(RFC 3986) 进行编码,而不是 application/x-www-form-urlencoded。
| 语言 | 正确函数 | 错误函数 |
|---|---|---|
| JavaScript | encodeURIComponent(cert) | — |
| Python | urllib.parse.quote(cert) | — |
| PHP | rawurlencode($cert) | urlencode($cert)(将空格编码为 +) |
| Java | URLEncoder.encode(cert, UTF_8).replace("+", "%20") | 仅使用 URLEncoder.encode(cert, UTF_8)(空格变为 +) |
如果您收到 "Invalid client certificate format. Expected PEM-encoded X.509 certificate." 错误,您的编码函数可能使用了 + 代替 %20 来编码空格。
响应 (201)
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 1800
}错误
| 状态码 | 描述 |
|---|---|
| 400 | 无效请求或 X-SSL-Client-Cert 头中缺少证书 |
| 401 | 凭证无效或证书无效 |
有关完整的身份验证指南和令牌续期模式,请参阅 身份验证指南。
X.509 证书必须以 URL 编码格式在 X-SSL-Client-Cert 头中发送。证书必须事先与您的账户关联。