在urllib3中使用证书

5 投票
3 回答
23880 浏览
提问于 2025-04-18 08:03

我刚开始学Python,正在用urllib3和一个API进行通信。我选择这个库而不是requests,是因为我想把我的应用放在GAE上。我的应用需要用到证书。当我发送数据时,出现了以下错误:

TypeError: __init__() got an unexpected keyword argument 'cert_reqs'

我该如何在我的urlopen调用中包含证书呢?下面是一些代码片段:

CA_CERTS = ('client-2048.crt', 'client-2048.key')

http = urllib3.PoolManager()
r = http.urlopen('POST', url, body=payload, headers={'X-Application': '???', 'Content-Type': 'application/x-www-form-urlencoded'}, cert_reqs='REQUIRED', ca_certs=CA_CERTS)
print r.status, r.data

3 个回答

0

在我的情况下,添加 User-Agent 头信息似乎有帮助,这比其他的回答更有效。

我不确定这是不是一种常见的现象,但我的服务器在使用自签名证书进行 HTTPS 请求时,如果没有 User-Agent,就会返回 403 - 访问被拒绝 的错误。

当我忽略证书(也就是说,使用一个空的 ssl.SSLContext)时,就不需要 User-Agent 头信息,请求就能成功。只有在通过 使用 ca_certs 参数传递自签名证书 时,我才需要添加 User-Agent

http = urllib3.PoolManager(cert_reqs='REQUIRED', ca_certs='/path/to/cacert.pem')
r = http.urlopen('GET', url, headers={'User-Agent': 'myapp/v0.1.0'})
print(r.data)

我找不到任何资料说明为什么在使用自签名证书时可能需要 User-Agent。对此点的任何解释都非常欢迎。

想了解更多关于 User-Agent 头的信息,可以点击 这里

1

你应该直接在创建 PoolManager() 的时候传入 cert_reqs='REQUIRED'ca_certs=CA_CERTS 这两个参数。

所以原来的例子可以改成这样:

CA_CERTS = ('client-2048.crt', 'client-2048.key')

http = urllib3.PoolManager(cert_reqs='REQUIRED', ca_certs=CA_CERTS)

r = http.urlopen('POST', url, body=payload, headers={'X-Application': '???', 'Content-Type': 'application/x-www-form-urlencoded'})
print r.status, r.data
10

你可以直接使用 HTTPSConnectionPool 这个层级来进行操作:

from urllib3.connectionpool import HTTPSConnectionPool
conn = HTTPSConnectionPool('httpbin.org', ca_certs='/etc/pki/tls/cert.pem', cert_reqs='REQUIRED')

或者,更简单的方法是通过 connection_from_url() 这个辅助函数来实现:

conn = urllib3.connection_from_url('https://httpbin.org', ca_certs='/etc/pki/tls/cert.pem', cert_reqs='REQUIRED')

需要注意的是,ca_certs 是一个证书包的文件名,用来验证远程服务器的证书。你可以使用 cert_filekey_file 来向远程服务器提供你的客户端证书:

conn = urllib3.connection_from_url('https://httpbin.org', cert_file='client-2048.crt', key_file='client-2048.key', ca_certs='/etc/pki/tls/cert.pem', cert_reqs='REQUIRED')

然后就可以发出你的请求了:

response = conn.request('POST', 'https://httpbin.org/post', fields={'field1':1234, 'field2':'blah'})
>>> print response.data
{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "field1": "1234",
    "field2": "blah"
  },
  "headers": {
    "Accept-Encoding": "identity",
    "Connection": "close",
    "Content-Length": "220",
    "Content-Type": "multipart/form-data; boundary=048b02ad15274fc485c2cb2b6a280034",
    "Host": "httpbin.org",
    "X-Request-Id": "92fbc1da-d83e-439c-9468-65d27492664f"
  },
  "json": null,
  "origin": "220.233.14.203",
  "url": "http://httpbin.org/post"
}

撰写回答