【问题标题】:SSLError in Requests when packaging as OS X .app打包为 OS X .app 时请求中出现 SSLError
【发布时间】:2013-06-08 22:57:20
【问题描述】:

我正在为 OS X 开发一个应用程序。该应用程序涉及使用安全连接通过 python 请求与服务器通信。

我能够运行我打算打包的 python 文件,并且它通过 SSL 连接成功。但是,当我用 py2app 打包文件并尝试运行它时,出现以下错误:

Traceback (most recent call last):
File "/Users/yossi/Documents/repos/drunken-octo-nemesis/dist/drunken-octo.app/Contents/Resources/__boot__.py", line 338, in <module>
    _run()
File "/Users/yossi/Documents/repos/drunken-octo-nemesis/dist/drunken-octo.app/Contents/Resources/__boot__.py", line 333, in _run
    exec(compile(source, path, 'exec'), globals(), globals())
File "/Users/yossi/Documents/repos/drunken-octo-nemesis/dist/drunken-octo.app/Contents/Resources/media_test.py", line 16, in <module>
    cmpbl.syncWithCloud()
File "src/compare_book_lists.pyc", line 172, in syncWithCloud
File "src/compare_book_lists.pyc", line 64, in checkMediaOnCloud
File "src/get_cloud_book_list.pyc", line 26, in getCloudFulfilledBookList
File "requests/api.pyc", line 55, in get
File "requests/api.pyc", line 44, in request
File "requests/sessions.pyc", line 354, in request
File "requests/sessions.pyc", line 460, in send
File "requests/adapters.pyc", line 250, in send
requests.exceptions.SSLError: [Errno 185090050] _ssl.c:340: error:0B084002:x509 certificate routines:X509_load_cert_crl_file:system lib
2013-06-12 11:39:49.119 drunken-octo[1656:707] drunken-octo Error

我能够成功打包我的应用程序的一部分。当目标文件依赖于请求链中的某个位置时,问题就开始了。

我正在使用 zc.buildout 来组织我的导入。因此,我在由 buildout 创建的本地 python 解释器中运行,所以不幸的是,如果不涉及修改系统 Python,任何修复都将更容易实现。不过,欢迎所有建议,我会尽力根据我的具体情况进行修改。

这只发生在我运行打包的应用程序时。有什么想法吗?

【问题讨论】:

    标签: python macos python-requests buildout py2app


    【解决方案1】:

    最简单的解决方法是将 py2app 选项添加到 setup.py 文件中:

    setup(
       ...
       options={
          'py2app':{
              'packages': [ 'requests' ]
           }
       }
    )
    

    这将整个包包含在应用程序包中,包括证书包。

    我已经提交了 issue for this in my py2app tracker,py2app 的未来版本将包含检测请求库使用的逻辑,并将自动复制证书包。

    【讨论】:

    • 嗨罗纳德,这个解决方法似乎不再有效。 py2app 有更新的消息吗?谢谢!
    【解决方案2】:

    Requests 使用一组证书来验证服务器身份。 这个包被保存(它必须是)在一个独立的文件中。通常请求附带它自己的捆绑包,但如果打包到单个文件中,捆绑包就会丢失。 您可以在您的应用程序旁边发布一个新捆绑包,或者让请求使用系统范围的证书。 (我不知道 OS X 将这个文件保存在哪里,但在我的 linux 机器上它是 /etc/ssl/certs/ca-certificates.crt

    要查看 requests 需要文件的位置,您可以这样做:

    import requests
    print(requests.certs.where())
    

    要更改请求查找捆绑包的位置,您可以传递带有字符串值的verify-参数:

    import requests
    requests.get("https://httpbin.org/", verify="path/to/your/bundle")
    

    如果您不想每次都传递参数,请创建一个会话并将其配置为使用您的包:

    import requests
    s = requests.Session()
    s.verify = "path/to/your/bundle"
    s.get("https://httpbin.org")
    

    【讨论】:

      【解决方案3】:

      之前接受的答案对我不起作用 - 也许 requests 的工作方式已经改变。

      为了解决这个问题,我更改了 setup.py 选项以包含证书 pem 文件所在的 certifi 包:

      OPTIONS = {'argv_emulation': True,'packages': ['certifi']}
      

      然后将此添加到 python 请求调用中:

      is_py2app = hasattr(sys, "frozen")
      pem_path = "lib/python2.7/certifi/cacert.pem" if is_py2app else None 
      
      ...
      
      requests.get(..., verify=pem_path)
      

      这在其他 Python 版本上可能会有所不同。

      【讨论】:

      • 是的,这对我的包裹来说也是重要的一步。感谢eAi
      • 仅供参考,默认情况下,我的 requests 包使用 certifi 包作为 cacert.pem 而不是它自己的包,但 certifi 在 site-package.zip 中而不是在打开的文件夹中。另一种可能是pem_path = "lib/python2.7/requests/cacert.pem" if is_py2app else None 使用与请求一起打包的那个(不需要添加证书)。但我想象的差别不大。
      • 嗨@eAi 如果这是一个愚蠢的问题,我很抱歉。但是is_py2app = hasattr(sys, "frozen") 行中的sys 是什么?
      【解决方案4】:

      我遇到了同样的问题,不得不将我的应用程序分发给可能没有在他们的 Mac 上安装 Python 或 certifi 包的用户。从这里的答案中汲取灵感,我想出了以下解决方案。

      第 1 步:从 https://www.openssl.org/source/ 下载 OpenSSL 包。找到/openssl-1.0.2n/certs/demo/ca-cert.pem 并将其放在与您的Python 程序相同的目录下(例如main.py)。

      第 2 步:像往常一样创建 setup.py,但在 DATA_FILES 列表中包含 ca-cert.pem。所以你的setup.py 应该是这样的:

      from setuptools import setup
      
      APP = ['main.py']
      DATA_FILES = ['ca-cert.pem']
      OPTIONS = {'argv_emulation': False}
      
      setup(
          app=APP,
          data_files=DATA_FILES,
          options={'py2app': OPTIONS},
          setup_requires=['py2app'],
      )
      

      第 3 步:使用 verify 参数,以便请求将使用您提供的证书文件。

      import requests
      requests.get("https://httpbin.org/", verify="ca-cert.pem")
      

      或者,您也可以创建一个会话,这样您就不必每次都指定verify

      import requests
      s = requests.Session()
      s.verify = "ca-cert.pem"
      s.get("https://httpbin.org")
      

      第 4 步:像往常一样使用 py2app 打包应用程序。生成的应用应该能够正常运行。

      python setup.py py2app
      

      【讨论】:

        猜你喜欢
        • 2020-06-10
        • 2016-02-05
        • 2021-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多