可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have soap service under Apache with ssl, suds works greate without ssl.
I have client certificate (my.crt and user.p12 files).
How I need to configure suds client ot make it work with service over https?
without certs i see
urllib2.URLError: <urlopen error [Errno 1] _ssl.c:499: error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure>
回答1:
It sounds like you want to authenticate using a client certificate, not a server certificate as was stated in some of the comments. I had the same issue and was able to write a custom transport for SUDS. Here's the code that works for me.
You'll need your certificates in PEM format for this to work; OpenSSL can easily perform this conversion, though I don't remember the exact syntax.
import urllib2, httplib, socket
from suds.client import Client
from suds.transport.http import HttpTransport, Reply, TransportError
class HTTPSClientAuthHandler(urllib2.HTTPSHandler):
def __init__(self, key, cert):
urllib2.HTTPSHandler.__init__(self)
self.key = key
self.cert = cert
def https_open(self, req):
#Rather than pass in a reference to a connection class, we pass in
# a reference to a function which, for all intents and purposes,
# will behave as a constructor
return self.do_open(self.getConnection, req)
def getConnection(self, host, timeout=300):
return httplib.HTTPSConnection(host,
key_file=self.key,
cert_file=self.cert)
class HTTPSClientCertTransport(HttpTransport):
def __init__(self, key, cert, *args, **kwargs):
HttpTransport.__init__(self, *args, **kwargs)
self.key = key
self.cert = cert
def u2open(self, u2request):
"""
Open a connection.
@param u2request: A urllib2 request.
@type u2request: urllib2.Requet.
@return: The opened file-like urllib2 object.
@rtype: fp
"""
tm = self.options.timeout
url = urllib2.build_opener(HTTPSClientAuthHandler(self.key, self.cert))
if self.u2ver() < 2.6:
socket.setdefaulttimeout(tm)
return url.open(u2request)
else:
return url.open(u2request, timeout=tm)
# These lines enable debug logging; remove them once everything works.
import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('suds.client').setLevel(logging.DEBUG)
logging.getLogger('suds.transport').setLevel(logging.DEBUG)
c = Client('https://YOUR_URL_HERE',
transport = HTTPSClientCertTransport('PRIVATE_KEY.pem',
'CERTIFICATE_CHAIN.pem'))
print c
回答2:
Another workaround is to use requests library as transport which has better support for ssl. This is what I'm using now to access SOAP services through https using suds:-
import requests
from suds.transport.http import HttpAuthenticated
from suds.transport import Reply, TransportError
class RequestsTransport(HttpAuthenticated):
def __init__(self, **kwargs):
self.cert = kwargs.pop('cert', None)
# super won't work because not using new style class
HttpAuthenticated.__init__(self, **kwargs)
def send(self, request):
self.addcredentials(request)
resp = requests.post(request.url, data=request.message,
headers=request.headers, cert=self.cert)
result = Reply(resp.status_code, resp.headers, resp.content)
return result
And then you can instantiate suds client as:-
headers = {"Content-TYpe" : "text/xml;charset=UTF-8",
"SOAPAction" : ""}
t = RequestsTransport(cert='/path/to/cert', **credentials)
client = Client(wsdl_uri, location=send_url, headers=headers,
transport=t))
Update
We're now using Zeep, which use requests
underneath.
回答3:
Based on @k4ml answer, I've only added the open()
which allows to fetch the WSDL using the certificate.
This method should fix the suds.transport.TransportError: HTTP Error 403: Forbidden
when trying to fetch a WSDL (at Client creation) served behind a HTTPS service.
import requests
from suds.transport.http import HttpAuthenticated
from suds.transport import Reply, TransportError
class RequestsTransport(HttpAuthenticated):
def __init__(self, **kwargs):
self.cert = kwargs.pop('cert', None)
# super won't work because not using new style class
HttpAuthenticated.__init__(self, **kwargs)
def open(self, request):
"""
Fetches the WSDL using cert.
"""
self.addcredentials(request)
resp = requests.get(request.url, data=request.message,
headers=request.headers, cert=self.cert)
result = io.StringIO(resp.content.decode('utf-8'))
return result
def send(self, request):
"""
Posts to service using cert.
"""
self.addcredentials(request)
resp = requests.post(request.url, data=request.message,
headers=request.headers, cert=self.cert)
result = Reply(resp.status_code, resp.headers, resp.content)
return result
Side note, I've also made a suggested edit to k4ml's answer, but it can take ages before it gets approved.
回答4:
Extending @k4ml solution, using cert + key
This will solve exceptions like:
requests.exceptions.SSLError: [SSL] PEM lib (_ssl.c:2599)
Solution:
import requests
from suds.client import Client
from suds.transport.http import HttpAuthenticated
from suds.transport import Reply, TransportError
class RequestsTransport(HttpAuthenticated):
def __init__(self, **kwargs):
self.cert = kwargs.pop('cert', None)
HttpAuthenticated.__init__(self, **kwargs)
def send(self, request):
self.addcredentials(request)
resp = requests.post(
request.url,
data=request.message,
headers=request.headers,
cert=self.cert,
verify=True
)
result = Reply(resp.status_code, resp.headers, resp.content)
return result
t = RequestsTransport(cert=('<your cert.pem path>', 'your key.pem path'))
headers = {"Content-Type": "text/xml;charset=UTF-8", "SOAPAction": ""}
client = Client(wsdl_url, headers=headers, transport=t)
回答5:
SSL security feature is auto enabled python 2.7.9+ which breaks suds and other python libraries. I am sharing a patch which can fix this:
Locate you suds library and replace u2handlers function in suds/trasnport/http.py file with following line:
import ssl
def u2handlers(self):
"""
Get a collection of urllib handlers.
@return: A list of handlers to be installed in the opener.
@rtype: [Handler,...]
"""
handlers = []
unverified_context = ssl.create_default_context()
unverified_context.check_hostname = False
unverified_context.verify_mode = ssl.CERT_NONE
unverified_handler = urllib2.HTTPSHandler(context=unverified_context)
handlers.append(unverified_handler)
handlers.append(urllib2.ProxyHandler(self.proxy))
#handlers.append(urllib2.ProxyHandler(self.proxy))
return handlers
Note: It's not a recommended way of doing it.