使用Dart调用Poloniex V2 API遭遇401身份验证错误求助
问题:Dart调用Poloniex V2 API认证接口持续返回401错误
我在使用Dart调用Poloniex V2 API的认证接口时,始终收到401错误。已严格按照官方认证文档的要求实现,确认签名和请求载荷(payload)均正确,但问题仍未解决。求有对接经验的开发者协助排查问题。
排查关键点
- 时间戳一致性问题:检查本地时间与Poloniex服务器时间差是否超过30秒,API会拒绝时间偏差过大的请求;确认
signTimestamp使用的是毫秒级时间戳(部分API可能要求秒级,需对照官方文档),且用UTC时间而非本地时间避免时区偏差。 - 签名生成的参数匹配问题:
- 在
generateSignatureRequestString的GET分支中,当前使用paramMap[key].toString(),但sortedParamMap已将值转成字符串,应改为sortedParamMap[key],避免值格式不一致导致签名错误。 - 确认URI编码完全符合要求:
Uri.encodeComponent需处理所有特殊字符,包括空格、中文等,确保编码后的参数与签名生成时的参数完全一致。
- 在
- 路径与URL一致性:签名生成时使用的
path(如/accounts)必须与实际请求的URL路径完全一致,不能存在大小写错误或额外拼接的参数。 - SecretKey解码正确性:确认你的SecretKey是Base64编码格式,
base64Decode(secretKey)才能正确生成密钥字节;若SecretKey是原始字符串,此步骤会导致签名完全错误。 - 请求头完整性:检查所有必填认证头是否齐全:
key、signTimestamp、signatureMethod、signatureVersion、signature,无拼写错误或遗漏。
相关代码实现
import 'dart:convert'; import 'package:crypto/crypto.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; const String poloniexPrivateApiKey = "xxxxxxxxxx"; const String poloniexPrivateSecretKey = "xxxxxxxx"; const String url = "https://api.poloniex.com"; const String publicApiUrl = "https://poloniex.com/public"; class PoloniexApiClient { static String v2Accounts = "/accounts"; static String v2Orders = "/orders"; static String v2Markets = "/markets"; String apiKey; String secretKey; String host; PoloniexApiClient({this.apiKey = poloniexPrivateApiKey, this.secretKey = poloniexPrivateSecretKey, this.host = url}); Future accounts() async { try { Map<String, dynamic> map = {}; var response = await PoloniexSignatureHelper.generateResponse(apiKey, secretKey, host, v2Accounts, map); debugPrint("accountResponse: ${response.toString()}"); } catch (e) { debugPrint(e.toString()); } } } class PoloniexSignatureHelper { static String contentType = "Content-Type"; static String jsonType = "application/json"; static String requestMethodGet = "GET"; static String requestMethodPost = "POST"; static String requestMethodDelete = "DELETE"; static String headerTimeStamp = "signTimestamp"; static String headerKey = "key"; static String headerSignMethod = "signatureMethod"; static String headerSignVersion = "signatureVersion"; static String headerSignature = "signature"; static String signatureMethodValue = "HmacSHA256"; static String signatureVersionValue = "2"; static Future<String> generateResponse(String apiKey, secretKey, host, path, Map<String, dynamic> paramMap) async { String timestamp = DateTime.now().millisecondsSinceEpoch.toString(); String payload = generateSignatureRequestString(requestMethodGet, path, timestamp, paramMap); print(payload); String signature = generateSignature(secretKey, payload); print(signature); String response = await executeRequest(host, path, apiKey, timestamp, signature, paramMap); print(response); return response; } static String generateSignatureRequestString(String method, path, timestamp, Map<String, dynamic> paramMap) { var sortedParamMap = sortMapASCII(paramMap, timestamp); var encoded; if (method == requestMethodGet) { encoded = sortedParamMap.keys .map((key) => "${Uri.encodeComponent(key)}=${Uri.encodeComponent(paramMap[key].toString())}") .join("&"); } else { sortedParamMap.remove(headerTimeStamp); var requestBody = jsonEncode(sortedParamMap); encoded = "requestBody=$requestBody&signTimestamp=$timestamp"; } var requestString = method + "\n" + path + "\n" + encoded; return requestString; } static String generateSignature(String secretKey, String requestString) { final keyBytes = base64Decode(secretKey); final dataBytes = utf8.encode(requestString); final hMacBytes = Hmac(sha256, keyBytes).convert(dataBytes).bytes; final hMacBase64 = base64Encode(hMacBytes); var sign = hMacBase64; return sign; } static Future<String> executeRequest( String host, path, apiKey, timestamp, signature, Map<String, dynamic> paramMap) async { String url = host + path; if (paramMap.isNotEmpty && paramMap.length > 0) { var queryStringBuffer = "?"; paramMap.forEach((key, value) { queryStringBuffer = queryStringBuffer + key + "=" + value.toString() + "&"; }); String queryString = queryStringBuffer.substring(0, queryStringBuffer.length - 1); url = url + queryString; } print(url); Map<String, String> _headers = { // contentType: jsonType, // contentType: "application/x-www-form-urlencoded", headerKey: apiKey, headerSignMethod: signatureMethodValue, headerSignVersion: signatureVersionValue, headerTimeStamp: timestamp, headerSignature: signature }; var result; try { var response = await http.get(Uri.parse(url), headers: _headers); // if (response.statusCode == 200) { // String data = response.body; // result = data; // } result = response.statusCode.toString(); // return 401?? } catch (e) { result = e.toString(); } return result; } static Map<String, dynamic> sortMapASCII(Map<String, dynamic> paramMap, timestamp) { paramMap.addAll({headerTimeStamp: timestamp}); var paramKeys = paramMap.keys.map((key) => "$key").toList(); paramKeys.sort(((a, b) => a.compareTo(b))); Map<String, dynamic> sortedParamMap = {}; paramKeys.forEach((key) => sortedParamMap.addAll({"$key": "${paramMap[key]}"})); print(sortedParamMap); return sortedParamMap; } }
内容的提问来源于stack exchange,提问作者ASIAMINOR
相关产品推荐
相关产品推荐

