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

NetSuite OAuth 1.0 Python调用返回INVALID_LOGIN_ATTEMPT错误求助

解决NetSuite REST API Python调用401 "Invalid login attempt"问题

先从这几个核心点排查(对应你怀疑的签名/时间戳问题):

1. 时间戳必须精准同步

NetSuite对请求时间戳的校验卡得很严,误差超过5分钟直接拒掉:

  • 把本地系统时间同步到UTC时间,别用本地时区时间。
  • Python里生成时间戳要用UTC,比如用str(int(datetime.datetime.utcnow().timestamp()))生成秒级时间戳,或者按YYYY-MM-DDTHH:MM:SSZ格式生成,和Postman里的时间戳格式保持一致。

2. 签名生成要和Postman完全对齐

签名是最容易出错的地方,直接对比Postman的签名生成步骤:

  • 签名字符串必须按HTTP_METHOD&编码后的请求URL&编码后的排序参数构造,参数要按ASCII顺序排序,每个部分都要用urllib.parse.quote做URL编码,记得加safe=''避免遗漏特殊字符。
  • 用HMAC-SHA256算法,密钥是你的NetSuite Secret Key,加密后转Base64,最后还要对签名本身做URL编码。
  • 把Python生成的签名、签名字符串和Postman里的复制出来逐字符对比,找差异。

3. 请求头不能缺项或格式错

确保所有认证头和Postman完全一致:

  • Authorization头的格式必须严格符合OAuth 1.0规范,每个参数都要用双引号括起来,逗号分隔,别漏了oauth_version="1.0"和oauth_signature_method="HMAC-SHA256"。
  • 别忘了加Content-Type: application/json和Prefer: transient(如果Postman里有)。

4. Nonce必须每次请求唯一

Nonce是随机字符串,每次请求都要生成新的,用uuid.uuid4().hex就行,别重复用同一个值。

修正后的代码示例(关键部分)

import datetime
import hmac
import hashlib
import base64
import urllib.parse
import uuid
import requests

# 替换成你的配置
consumer_key = "你的Consumer Key"
consumer_secret = "你的Secret Key"
account_id = "你的账户ID"
base_url = f"https://{account_id}.suitetalk.api.netsuite.com/services/rest/record/v1/invoice"

# 生成UTC秒级时间戳
timestamp = str(int(datetime.datetime.utcnow().timestamp()))
# 生成唯一Nonce
nonce = uuid.uuid4().hex

# 构造签名基字符串(GET请求示例)
http_method = "GET"
encoded_url = urllib.parse.quote(base_url, safe='')
# 按ASCII顺序排序参数
params = {
    "oauth_consumer_key": consumer_key,
    "oauth_nonce": nonce,
    "oauth_signature_method": "HMAC-SHA256",
    "oauth_timestamp": timestamp,
    "oauth_version": "1.0"
}
sorted_params = sorted(params.items())
encoded_params = urllib.parse.quote('&'.join([f"{k}={v}" for k, v in sorted_params]), safe='')
signature_base = f"{http_method}&{encoded_url}&{encoded_params}"

# 生成并编码签名
signature = base64.b64encode(hmac.new(
    consumer_secret.encode('utf-8'),
    signature_base.encode('utf-8'),
    hashlib.sha256
).digest()).decode('utf-8')
encoded_signature = urllib.parse.quote(signature, safe='')

# 构造Authorization头
auth_header = (
    f'OAuth oauth_consumer_key="{consumer_key}", '
    f'oauth_nonce="{nonce}", '
    f'oauth_signature="{encoded_signature}", '
    f'oauth_signature_method="HMAC-SHA256", '
    f'oauth_timestamp="{timestamp}", '
    f'oauth_version="1.0"'
)

headers = {
    "Authorization": auth_header,
    "Content-Type": "application/json",
    "Prefer": "transient"
}

response = requests.get(base_url, headers=headers)
print(response.status_code)
print(response.text)

额外要查的点

  • 确认Python里的Consumer Key/Secret、账户ID和Postman里完全一样,别多打空格或拼错。
  • 如果是沙箱环境,base_url要换成沙箱的域名,比如https://{account_id}-sb1.suitetalk.api.netsuite.com。
  • 虽然你是管理员,但还是确认下API角色有没有发票读取权限,避免权限配置不一致。

内容的提问来源于stack exchange,提问作者sdh2000

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 02:50:30