【SSL证书校验问题】通过 monkey-patch 关掉 SSL 证书校验
标签:Python、SSL、monkey-patch、httpx、aiohttp、requests、OpenAI
1 为什么会有这篇文章?
在本地调试 OpenAI 代理、数据抓取、私有服务、访问外网 时,经常会碰到如下报错:
SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed
raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='api.openai.com', port=443): Max retries exceeded with url: /v1/models (Caused by SSLError(SSLError(1, '[SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1017)')))
File ".venv\lib\site-packages\httpx\_transports\default.py", line 118, in map_httpcore_exceptions| raise mapped_exc(message) from exc| httpx.ConnectError: All connection attempts failed
An error occurred: Expecting value: line 1 column 1 (char 0)
An error occurred during the request: HTTPSConnectionPool(host='en.wikipedia.org', port=443): Max retries exceeded with url: /w/api.php?list=search&srprop=&srlimit=1&limit=1&srsearch=langchain&format=json&action=query (Caused by ProxyError('Unable to connect to proxy', SSLError(SSLEOFError(8, '[SSL: UNEXPECTED_EOF_WHILE_READING] EOF occurred in violation of protocol (_ssl.c:1000)'))))
File ".venv\lib\site-packages\requests\adapters.py", line 698, in sendraise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='r.jina.ai', port=443): Max retries exceeded with url: /https://baijiahao.baidu.com (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate in certificate chain (_ssl.c:997)')))
为了快速定位 “是不是证书问题” 而不是“逻辑问题”,我们需要临时、零侵入地把所有 HTTP 客户端的 SSL 校验关掉。
把下面这段脚本调用后就能跑,调试结束直接删除,零副作用。
或者为了临时绕过所有SSL证书验证,使代码逻辑跑通。
2 完整代码(复制即用)
def monkey_patch():"""通过关闭 SSL/TLS 证书验证,解决 ssl 验证引发的 443 超时问题(注:临时调试使用,生产务必移除)"""import functools# 1) 全局标准库 SSL 上下文try:import ssl# 全局取消证书验证ssl._create_default_https_context = ssl._create_unverified_contextexcept Exception:pass# 2) httpx 同步 & 异步try:import httpxhttpx.Client.__init__ = functools.partialmethod(httpx.Client.__init__, verify=False)httpx.AsyncClient.__init__ = functools.partialmethod(httpx.AsyncClient.__init__, verify=False)except Exception:pass# 3) OpenAI 私有 AsyncHttpxClientWrappertry:import openai._base_client as bc_old = bc.AsyncHttpxClientWrapper.__init__def _new_init(self, *a, **k):k.setdefault("verify", False)return _old(self, *a, **k)bc.AsyncHttpxClientWrapper.__init__ = _new_initexcept Exception:pass# 4) aiohttptry:import aiohttpaiohttp.TCPConnector.__init__ = functools.partialmethod(aiohttp.TCPConnector.__init__, verify_ssl=False)except Exception:pass# 5) requeststry:import requestsfrom functools import wraps# 5-1) 修改 Session 实例默认属性_orig_init = requests.Session.__init__@wraps(_orig_init)def _patched_init(self):_orig_init(self)self.verify = Falserequests.Session.__init__ = _patched_init# 5-2) 快捷函数也兜底for name in ("get", "post", "put", "patch", "delete", "head", "options"):_orig = getattr(requests, name)setattr(requests,name,(lambda _o: lambda *a, **k: _o(*a, **dict(k, verify=False)))(_orig),)except Exception:pass
使用:
# 在业务逻辑最顶部调用即可
monkey_patch()
3 逐段技巧拆解
位置 | 技巧 | 一句话解释 |
---|---|---|
ssl._create_default_https_context = ... | 全局钩子 | 把标准库 HTTPS 默认上下文换成“不校验”。 |
functools.partialmethod | 一行改默认参数 | 不继承、不派生,直接把类方法的形参默认值改掉。 |
setdefault("verify", False) | 不覆盖显式传参 | 只在用户没传时兜底,传了 True 仍然生效。 |
@wraps | 栈信息不丢失 | 调试时能看到原函数名,而不是 <lambda> 。 |
except Exception: | 不吞系统信号 | 保留 KeyboardInterrupt 、SystemExit ,脚本可 Ctrl-C。 |
4 何时删除?
场景 | 建议 |
---|---|
单测/本地调试 | 保留 |
代码评审 / 上线 | 必须删除 |
CI / Docker | 用环境变量 SSL_NO_VERIFY=1 控制开关,而非硬编码 |
5 延伸阅读
- Python 官方文档:
ssl.create_default_context
- httpx 文档:
verify
- requests 文档:
Session.verify
一句话总结
通过侵入式修改,关掉全部 SSL 校验;调试完删掉,干净不留痕。