Python Zeep调用SOAP服务时遇PermissionError 13权限拒绝问题求助
Zeep客户端调用SOAP服务时PEM证书权限拒绝问题
当前Python实现代码
from requests import Session from zeep import Client from zeep.settings import Settings from zeep.transports import Transport from cryptography.hazmat.primitives.serialization import pkcs12 from cryptography.hazmat.primitives import serialization import tempfile session = Session() session.verify = True private_key, certificate, additional_certificates = pkcs12.load_key_and_certificates( open(pfx_cert_path, "rb").read(), cert_psw_b) # 修正原代码语法错误:补全括号并导入tempfile key_tmpfile = tempfile.NamedTemporaryFile(dir="C:/Users/USERA/Anaconda3/envs/env/") key_tmpfile.write( private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption(), ) ) key_tmpfile.flush() cert_tmpfile=tempfile.NamedTemporaryFile(dir="C:/Users/USERA/Anaconda3/envs/env/") cert_tmpfile.write( certificate.public_bytes(serialization.Encoding.PEM), ) cert_tmpfile.flush() session.cert = cert_tmpfile.name, key_tmpfile.name settings = Settings(raw_response=True) soap_client = Client(wsdl_url, transport=Transport(session=session), settings=settings)
SOAP调用方式
soap_client.service.UploadMethod({"body_field":"body_value","_soapheaders":{"head_field":"head_value"}})
错误栈信息
File "C:\Users\USERA\Anaconda3\envs\env\lib\site-packages\urllib3\connection.py", line 419, in connect self.sock = ssl_wrap_socket( File "C:\Users\USERA\Anaconda3\envs\env\lib\site-packages\urllib3\util\ssl_.py", line 413, in ssl_wrap_socket if keyfile and key_password is None and _is_key_file_encrypted(keyfile): File "C:\Users\USERA\Anaconda3\envs\env\lib\site-packages\urllib3\util\ssl_.py", line 472, in _is_key_file_encrypted with open(key_file, "r") as f: PermissionError: [Errno 13] Permission denied:
已成功实现的C#代码
CLIENT client = new CLIENT(); // Project -> Add Service Reference -> Advanced -> Add Web Reference -> https://service_domain/service.asmx -> Rename "CLIENT" string cert_path = "C:/PATH/Certificates/PKCS12_Credential.pfx"; string cert_psw_path = "C:/PATH/Certificates/psw_pfx.txt"; X509Certificate2 certificate = new X509Certificate2(cert_path, File.OpenText(cert_psw_path).ReadLine()); client.ClientCertificates.Add(certificate); client.Login("", ""); // 加载SOAP头 string xml_body = "C:/PATH/Body.txt"; client.UploadMethod(File.ReadAllText(xml_body));
解决方法
方案1:直接传递证书字节(无需临时文件)
绕过requests.cert仅支持文件路径的限制,通过自定义SSL上下文直接加载内存中的证书和密钥:
from requests import Session, adapters from zeep import Client from zeep.settings import Settings from zeep.transports import Transport from cryptography.hazmat.primitives.serialization import pkcs12 from cryptography.hazmat.primitives import serialization import ssl session = Session() session.verify = True # 加载PFX证书 private_key, certificate, additional_certificates = pkcs12.load_key_and_certificates( open(pfx_cert_path, "rb").read(), cert_psw_b) # 创建自定义SSL上下文 ssl_context = ssl.create_default_context() # 直接传入证书和私钥的字节数据 ssl_context.load_cert_chain( certfile=None, keyfile=None, cert_data=certificate.public_bytes(serialization.Encoding.PEM), key_data=private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption() ) ) # 添加额外证书(如果有) for cert in additional_certificates: ssl_context.load_verify_locations(cadata=cert.public_bytes(serialization.Encoding.PEM)) # 给session绑定自定义SSL上下文 session.mount("https://", adapters.HTTPAdapter(ssl_context=ssl_context)) settings = Settings(raw_response=True) soap_client = Client(wsdl_url, transport=Transport(session=session), settings=settings)
方案2:调整临时文件权限与生命周期
如果必须使用临时文件,需确保文件权限足够且不被提前回收:
import tempfile import os import stat # 创建临时文件时设置delete=False,避免自动删除 key_tmpfile = tempfile.NamedTemporaryFile(dir="C:/Users/USERA/Anaconda3/envs/env/", delete=False) # 设置文件全局可读权限(Windows下适配) os.chmod(key_tmpfile.name, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) key_tmpfile.write( private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption(), ) ) key_tmpfile.close() cert_tmpfile = tempfile.NamedTemporaryFile(dir="C:/Users/USERA/Anaconda3/envs/env/", delete=False) os.chmod(cert_tmpfile.name, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) cert_tmpfile.write(certificate.public_bytes(serialization.Encoding.PEM)) cert_tmpfile.close() session.cert = cert_tmpfile.name, key_tmpfile.name # 调用服务后手动清理临时文件 try: response = soap_client.service.UploadMethod({"body_field":"body_value","_soapheaders":{"head_field":"head_value"}}) finally: os.unlink(key_tmpfile.name) os.unlink(cert_tmpfile.name)
方案3:降级urllib3版本
部分新版本urllib3在Windows下处理临时文件权限存在兼容问题,可尝试降级到1.26.x稳定版本:
pip install urllib3==1.26.15
内容的提问来源于stack exchange,提问作者LNRD.CLL
相关产品推荐
相关产品推荐

