API 鉴权说明
概述
本 API 使用 API Key + HMAC 签名 的鉴权方案,确保请求的安全性和完整性。
认证方式
1. API Key 认证
最简单的认证方式,适用于服务端到服务端的调用。
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://api.procure-core.com/v1/purchase-requests2. OAuth 2.0
标准的 OAuth 2.0 流程,适用于第三方应用集成。
授权码流程
# 1. 获取授权码
https://api.procure-core.com/oauth/authorize?
response_type=code&
client_id=YOUR_CLIENT_ID&
redirect_uri=YOUR_REDIRECT_URI&
scope=read:purchase-requests write:orders&
state=random_string
# 2. 交换访问令牌
curl -X POST https://api.procure-core.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=AUTHORIZATION_CODE" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "redirect_uri=YOUR_REDIRECT_URI"客户端凭证流程
curl -X POST https://api.procure-core.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "scope=read:suppliers write:contracts"3. JWT 令牌
用于用户会话认证,前端应用常用。
// 获取 JWT 令牌
const response = await fetch('https://api.procure-core.com/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: 'user@company.com',
password: 'your_password'
})
});
const { token } = await response.json();
// 使用令牌调用 API
const apiResponse = await fetch('https://api.procure-core.com/v1/profile', {
headers: {
'Authorization': `Bearer ${token}`
}
});权限范围 (Scopes)
可用范围
{
"read:purchase-requests": "读取采购需求",
"write:purchase-requests": "创建和修改采购需求",
"read:suppliers": "读取供应商信息",
"write:suppliers": "管理供应商信息",
"read:orders": "读取订单信息",
"write:orders": "创建和修改订单",
"read:contracts": "读取合同信息",
"write:contracts": "管理合同信息",
"read:reports": "读取报表数据",
"admin": "管理员权限"
}权限检查
API 会根据令牌的权限范围检查访问权限:
{
"error": {
"code": "insufficient_scope",
"message": "The request requires higher privileges than provided by the access token.",
"required_scope": "write:purchase-requests"
}
}令牌管理
令牌刷新
访问令牌有有效期,需要定期刷新:
curl -X POST https://api.procure-core.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "refresh_token=YOUR_REFRESH_TOKEN" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"令牌撤销
主动撤销不再使用的令牌:
curl -X POST https://api.procure-core.com/oauth/revoke \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "token=ACCESS_TOKEN_OR_REFRESH_TOKEN" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"安全最佳实践
1. 安全存储
- 永远不要在前端代码中硬编码密钥
- 使用环境变量存储敏感信息
- 定期轮换 API 密钥
2. HTTPS 传输
所有 API 调用必须使用 HTTPS:
# ❌ 错误 - 不安全
curl http://api.procure-core.com/v1/orders
# ✅ 正确 - 安全
curl https://api.procure-core.com/v1/orders3. 令牌有效期
- 访问令牌:1小时有效期
- 刷新令牌:30天有效期
- API 密钥:永不过期(建议定期轮换)
4. IP 白名单
生产环境建议配置 IP 白名单:
{
"api_key": "ak_live_xxx",
"allowed_ips": [
"192.168.1.100",
"10.0.0.0/8"
]
}错误处理
认证错误
{
"error": {
"code": "unauthorized",
"message": "Invalid or missing authentication credentials"
}
}权限错误
{
"error": {
"code": "forbidden",
"message": "Insufficient permissions for this operation"
}
}令牌过期
{
"error": {
"code": "token_expired",
"message": "The access token has expired"
}
}代码示例
Node.js 示例
const axios = require('axios');
class ProcureBotAPI {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseURL = 'https://api.procure-core.com/v1';
this.client = axios.create({
baseURL: this.baseURL,
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
});
// 响应拦截器处理认证错误
this.client.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 401) {
console.error('Authentication failed. Please check your API key.');
}
return Promise.reject(error);
}
);
}
async getPurchaseRequests() {
const response = await this.client.get('/purchase-requests');
return response.data;
}
async createOrder(orderData) {
const response = await this.client.post('/orders', orderData);
return response.data;
}
}
// 使用示例
const api = new ProcureBotAPI(process.env.PROCUREBOT_API_KEY);Python 示例
import requests
import os
class ProcureBotAPI:
def __init__(self, api_key):
self.api_key = api_key
self.base_url = 'https://api.procure-core.com/v1'
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
})
def get_suppliers(self):
response = self.session.get(f'{self.base_url}/suppliers')
response.raise_for_status()
return response.json()
def create_purchase_request(self, request_data):
response = self.session.post(
f'{self.base_url}/purchase-requests',
json=request_data
)
response.raise_for_status()
return response.json()
# 使用示例
api = ProcureBotAPI(os.getenv('PROCUREBOT_API_KEY'))
suppliers = api.get_suppliers()PHP 示例
<?php
class ProcureBotAPI {
private $apiKey;
private $baseUrl = 'https://api.procure-core.com/v1';
public function __construct($apiKey) {
$this->apiKey = $apiKey;
}
private function makeRequest($method, $endpoint, $data = null) {
$url = $this->baseUrl . $endpoint;
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
]
];
if ($method === 'POST') {
$options[CURLOPT_POST] = true;
$options[CURLOPT_POSTFIELDS] = json_encode($data);
}
$curl = curl_init();
curl_setopt_array($curl, $options);
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
if ($httpCode >= 400) {
throw new Exception("API Error: HTTP {$httpCode}");
}
return json_decode($response, true);
}
public function getContracts() {
return $this->makeRequest('GET', '/contracts');
}
public function createContract($contractData) {
return $this->makeRequest('POST', '/contracts', $contractData);
}
}
// 使用示例
$api = new ProcureBotAPI($_ENV['PROCUREBOT_API_KEY']);
$contracts = $api->getContracts();
?>提示: 建议在开发环境中使用测试 API 密钥,生产环境中使用正式密钥。测试密钥以
ak_test_开头,正式密钥以ak_live_开头。