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

Zeep调用带Header和Timestamp的SOAP接口遇安全错误求助

问题描述

我尝试使用Zeep调用KBOpub的SOAP服务,接口要求提供用户名、密码、Nonce(PasswordDigest)、时间戳及请求头。在SoapUI中可正常调用,但用Zeep实现时,服务器返回错误:

Fault: A security error was encountered when verifying the message

我推测问题出在请求头的设置上,但无法找到正确的添加方式。

服务文档提供的请求示例如下:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:mes="http://economie.fgov.be/KBOpub/webservices/v1/messages"
xmlns:dat="http://economie.fgov.be/KBOpub/webservices/v1/datamodel">
    <soapenv:Header>
        <wsse:Security>
            <wsu:Timestamp>
                <wsu:Created>2009-09-07T11:27:10.748Z</wsu:Created>
                <wsu:Expires>2009-09-07T11:32:10.748Z</wsu:Expires>
            </wsu:Timestamp>
            <wsse:UsernameToken>
                <wsse:Username>userid</wsse:Username>
                <wsse:Password>x3+DQlYgeVm3BAkobZivkDJ13zo=</wsse:Password>
                <wsse:Nonce>ENp2ha7j2Ar9cvWQeUybTQ==</wsse:Nonce>
                <wsu:Created>2009-09-07T11:27:10.716Z</wsu:Created>
            </wsse:UsernameToken>
       </wsse:Security>
       <mes:RequestContext>
           <mes:Id>c1576d0a-e762-40fe-abf9-ec3f2102650b</mes:Id>
           <mes:Language>fr</mes:Language>
       </mes:RequestContext>
    </soapenv:Header>
    <soapenv:Body>
        <mes:ReadEnterpriseRequest>
            <dat:EnterpriseNumber>0314595348</dat:EnterpriseNumber>
        </mes:ReadEnterpriseRequest>
    </soapenv:Body>
</soapenv:Envelope>

我参考其他方案尝试了以下代码,但仍收到相同的安全错误:

from zeep.wsse.username import UsernameToken
import datetime
from zeep.wsse.utils import WSU
from zeep.xsd import Element, ComplexType, String, Sequence
from zeep import Client

wsdl_url = 'https://kbopub.economie.fgov.be/kbopubws110000/services/wsKBOPub?wsdl'

username = '***'
password = '******'

timestamp_token = WSU.Timestamp()
today_datetime = datetime.datetime.today()
expires_datetime = today_datetime + datetime.timedelta(minutes=5)
timestamp_elements = [
        WSU.Created(today_datetime.strftime("%Y-%m-%dT%H:%M:%S.%fZ")),
        WSU.Expires(expires_datetime.strftime("%Y-%m-%dT%H:%M:%S.%fZ"))]

timestamp_token.extend(timestamp_elements)
user_name_token = UsernameToken(username, password, timestamp_token=timestamp_token, use_digest=True)
client = Client(wsdl_url, wsse=user_name_token)
client.set_ns_prefix('mes', "http://economie.fgov.be/kbopub/webservices/v1/messages")

headerQ = Element('{http://schemas.xmlsoap.org/soap/envelope.xsd}Header', ComplexType([
  Element('{http://economie.fgov.be/kbopub/webservices/v1/messages.xsd}RequestContext', ComplexType(
    Sequence([
      Element('{http://economie.fgov.be/kbopub/webservices/v1/messages.xsd}Id', String()),
      Element('{http://economie.fgov.be/kbopub/webservices/v1/messages.xsd}Language', String())
    ]))
  )
]))
header_value = headerQ({'Id': 'whatever', 'Language': 'fr'})
client.set_default_soapheaders([header_value])

client.service.ReadEnterprise(EnterpriseNumber='0314595348')
解决方案

你的问题集中在时间戳格式、RequestContext构造细节上,对照SoapUI的请求规范调整即可解决:

1. 修正时间戳的时区与格式

服务要求UTC时间,且需要保留三位毫秒精度,你之前用本地时间生成的格式不符合要求,改用utcnow()并调整格式化规则:

utc_now = datetime.datetime.utcnow()
# 保留三位毫秒,结尾加Z表示UTC
formatted_now = utc_now.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
expires = utc_now + datetime.timedelta(minutes=5)
formatted_expires = expires.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"

2. 简化RequestContext的构造

无需手动定义复杂类型,直接用Zeep从WSDL中生成的类型创建请求头,避免命名空间错误:

# 替换原有的手动Header构造代码
request_context_type = client.get_type('mes:RequestContext')
header_value = request_context_type(Id='c1576d0a-e762-40fe-abf9-ec3f2102650b', Language='fr')
client.set_default_soapheaders([header_value])

3. 确保WS-Security参数的规范生成

Zeep的UsernameToken默认会处理Nonce和PasswordDigest的生成,但要明确指定时间戳参数,避免自动生成的时间与服务要求不符。

修正后的完整代码

from zeep.wsse.username import UsernameToken
import datetime
from zeep import Client

wsdl_url = 'https://kbopub.economie.fgov.be/kbopubws110000/services/wsKBOPub?wsdl'

username = '你的用户名'
password = '你的密码'

# 生成符合要求的UTC时间戳
utc_now = datetime.datetime.utcnow()
formatted_now = utc_now.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
expires = utc_now + datetime.timedelta(minutes=5)
formatted_expires = expires.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"

# 初始化UsernameToken,自动处理Nonce、Digest和时间戳
user_token = UsernameToken(
    username,
    password,
    created=formatted_now,
    expires=formatted_expires,
    use_digest=True
)

client = Client(wsdl_url, wsse=user_token)
client.set_ns_prefix('mes', "http://economie.fgov.be/kbopub/webservices/v1/messages")

# 用WSDL生成的类型创建RequestContext
request_context = client.get_type('mes:RequestContext')(
    Id='c1576d0a-e762-40fe-abf9-ec3f2102650b',
    Language='fr'
)
client.set_default_soapheaders([request_context])

# 调用接口
response = client.service.ReadEnterprise(EnterpriseNumber='0314595348')
print(response)

额外排查建议

如果仍报错,抓包对比Zeep生成的请求和SoapUI的请求,重点检查:

  • <wsse:Security>节点的命名空间是否为http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd
  • Nonce、Created时间、PasswordDigest的格式是否与SoapUI生成的一致
  • RequestContext是否和Security节点同级别,都在<soapenv:Header>下

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 12:35:08