You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 04:32:08