集成指南
认证
概述
Avista API 使用基于 OAuth 2.0 和 X.509 证书 (mTLS) 的认证方式。这种分层安全模型确保只有拥有有效证书的授权客户端才能访问 API。
为什么使用 mTLS?
mTLS(双向 TLS)与简单令牌相比提供更高级别的安全性:
- 双向认证:客户端和服务端互相验证身份
- 不可否认性:与账户关联的证书确保可追溯性
- 防止凭证被盗:即使 clientId/clientSecret 泄露,攻击者仍需要证书
前提条件
在开始之前,您需要:
通过 Avista 门户获取您的客户端证书。证书必须为 PEM 格式,并将关联到您的账户。
在管理面板中申请您的凭证(clientId 和 clientSecret)。
配置您的环境,以在 X-SSL-Client-Cert 请求头中发送证书。
X.509 证书必须在使用前关联到您的账户。未关联的证书即使技术上有效也会被拒绝。
认证端点
POST /api/auth/token
生成一个有效期为 30 分钟(1800 秒)的 JWT 访问令牌。
X.509 证书必须以 URL 编码 格式在 X-SSL-Client-Cert 请求头中发送。系统将验证证书的 SHA256 指纹与关联到账户的记录是否匹配。
请求
curl -X POST https://api.avista.global/api/auth/token \
-H "Content-Type: application/json" \
-H "X-SSL-Client-Cert: -----BEGIN%20CERTIFICATE-----%0AMIIB..." \
-d '{
"clientId": "account-93-550e8400",
"clientSecret": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
}'响应 (201 Created)
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 1800
}实践示例:Node.js
安装
npm install axios完整代码
const axios = require('axios');
const fs = require('fs');
// Load X.509 certificate
const certificate = fs.readFileSync('./client-cert.pem', 'utf8');
const encodedCert = encodeURIComponent(certificate);
// Request configuration
const config = {
method: 'post',
url: 'https://api.avista.global/api/auth/token',
headers: {
'Content-Type': 'application/json',
'X-SSL-Client-Cert': encodedCert
},
data: {
clientId: process.env.AVISTA_CLIENT_ID,
clientSecret: process.env.AVISTA_CLIENT_SECRET
}
};
// Make request
async function getToken() {
try {
const response = await axios(config);
console.log('Token obtained successfully!');
console.log('Expires in:', response.data.expires_in, 'seconds');
return response.data.access_token;
} catch (error) {
console.error('Error obtaining token:', error.response?.data || error.message);
throw error;
}
}
getToken();实践示例:Python
安装
pip install requests完整代码
import os
import requests
import urllib.parse
# Load and encode certificate
with open('client-cert.pem', 'r') as f:
certificate = f.read()
encoded_cert = urllib.parse.quote(certificate)
# Request configuration
url = 'https://api.avista.global/api/auth/token'
headers = {
'Content-Type': 'application/json',
'X-SSL-Client-Cert': encoded_cert
}
payload = {
'clientId': os.environ.get('AVISTA_CLIENT_ID'),
'clientSecret': os.environ.get('AVISTA_CLIENT_SECRET')
}
# Make request
try:
response = requests.post(url, json=payload, headers=headers)
response.raise_for_status()
data = response.json()
print('Token obtained successfully!')
print(f"Expires in: {data['expires_in']} seconds")
except requests.exceptions.RequestException as e:
print(f'Error obtaining token: {e}')
if hasattr(e.response, 'text'):
print(f'Response: {e.response.text}')使用令牌
获取令牌后,在所有请求的 Authorization 请求头中包含它:
curl -X GET https://api.avista.global/api/balance \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."令牌续期
令牌在 30 分钟 后过期。请在您的应用程序中实现自动续期逻辑以避免中断。
推荐策略
class TokenManager {
constructor(clientId, clientSecret, certificatePath) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.certificatePath = certificatePath;
this.token = null;
this.expiresAt = null;
}
async getValidToken() {
// Check if the token is still valid (with a 30-second margin)
if (this.token && this.expiresAt && Date.now() < this.expiresAt - 30000) {
return this.token;
}
// Renew the token
return await this.refreshToken();
}
async refreshToken() {
const response = await this.requestNewToken();
this.token = response.access_token;
this.expiresAt = Date.now() + (response.expires_in * 1000);
return this.token;
}
async requestNewToken() {
const fs = require('fs');
const axios = require('axios');
const certificate = fs.readFileSync(this.certificatePath, 'utf8');
const encodedCert = encodeURIComponent(certificate);
const response = await axios.post('https://api.avista.global/api/auth/token', {
clientId: this.clientId,
clientSecret: this.clientSecret
}, {
headers: {
'Content-Type': 'application/json',
'X-SSL-Client-Cert': encodedCert
}
});
return response.data;
}
}
// Usage
const tokenManager = new TokenManager(
process.env.AVISTA_CLIENT_ID,
process.env.AVISTA_CLIENT_SECRET,
'./client-cert.pem'
);
// In any request
const token = await tokenManager.getValidToken();证书验证
系统对证书执行以下验证:
- 有效的 PEM 格式:证书必须为 PEM 格式并经过 URL 编码
- 账户关联:证书的 SHA256 指纹必须已注册并关联到您的账户
- 凭证匹配:证书必须属于与 OAuth 凭证相同的账户
未关联或关联到其他账户的证书将被拒绝,即使技术上有效。
常见错误
400 Bad Request
原因: 证书缺失或格式错误
{
"statusCode": 400,
"message": "Certificado ausente no header X-SSL-Client-Cert"
}解决方案: 请检查:
- 证书是否为 PEM 格式
- 证书是否经过 URL 编码(使用
encodeURIComponent()) - 请求中是否包含
X-SSL-Client-Cert请求头
401 Unauthorized
原因: 凭证无效或证书未授权
{
"statusCode": 401,
"message": "Credenciais inválidas ou certificado inválido"
}解决方案: 请检查:
clientId和clientSecret是否正确- 证书是否已在 Avista 门户中关联到您的账户
- 证书是否与正在使用的 OAuth 凭证匹配
403 Forbidden
原因: 证书未关联到账户
{
"statusCode": 403,
"message": "Certificado não vinculado à conta"
}解决方案: 联系 Avista 技术支持将证书关联到您的账户。