【问题标题】:How to authenticate with requests module using a trust store?如何使用信任库对请求模块进行身份验证?
【发布时间】:2013-02-05 15:35:57
【问题描述】:

我目前正在为我编写的编译器编写一个 python 语言插件,它可以自动执行 RESTful API 的 http 调用。我已经设法使用 socket 和 ssl 模块使登录/身份验证正常工作,但是这种低级方法似乎会在解析响应以获取身份验证令牌和机密时产生潜在问题。 requests 模块似乎很流行/高效,但是,我似乎无法让它正常运行以满足我的特定身份验证需求。我正在使用 .pem 文件(仅包含一个公钥)形式的信任库,该文件是从用于验证 Java 插件的 .jks 文件转换而来的。服务器期望用户名和密码以 json 格式提交到请求正文中。这是我一直在尝试使用的代码:

#Server and login data
...
host = 'localhost'
port = 8443
pem_file = "C:\\Users\\aharasta\\pycert.pem"

#Digest password with MD5 algorithm
m = hashlib.md5()
m.update(password)
encrypted_password = m.hexdigest()

url = <url>
data = {'userid': user_name, 'password': encrypted_password}
json_data = json.dumps(data)
headers = {'Content-type': 'application/json', 'Accept': 'text/plain', 'Content \                     
          Length': len(json_data)} 

r = requests.post(url, headers = headers, data = json_data, cert = pem_file)
print(r)

执行时,此代码将引发 SSL 错误,指出“证书验证失败”。如果我添加参数verify = Falseverify = pem_file,我将收到来自服务器的404 响应。我还应该注意,当我在调试模式下启动服务器并执行请求(使用验证参数之一)时,它永远不会进入服务器的身份验证方法或任何与此相关的方法。 非常感谢您对此事的任何见解或帮助!

【问题讨论】:

    标签: python authentication python-requests truststore urllib3


    【解决方案1】:

    首先,您发布的内容存在几个问题:

    • 您指定了hostport,但没有给出示例url,因此我们可以猜测您正在使用本地部署进行测试,并且实际上可以查看到服务器的请求。我不确定您在谈论什么方法,但是如果您使用类似于 Flask 的东西进行服务器开发,您可能不想将身份验证作为 JSON 编码数据发送。有 Authentication 标头是有原因的,请求有 Authentication 处理程序是有原因的。 ;-)

    • 您不应自己指定 Content-Length 标头。 requests 将为您执行此操作。除此之外,您指定不正确(根据您发布的内容),因此 404 可能来自接收您的服务器无法识别的标头。

    现在,应该没有理由指定verify=False,您可以指定cert=pem_fileverify=pem_file。一个或两个都可以,但你永远不应该使用verify=False

    最后,引发的SSLError 告诉您,您提供的pem 文件与服务器指定的文件不匹配。考虑到这一点,您可能需要检查本地服务器的设置。 Requests 本身不处理证书验证,但 urllib3 提供了它。我们只是根据您提供的参数进行设置。我怀疑这是 urllib3 的错,因为它引发了一个来自标准库的 ssl 模块的SSLError

    编辑

    解释是在documentation 中指定*.pem 文件与cert 是无效的。您必须使用verify='/path/to/file.pem' 才能正确执行此操作。

    编辑#2

    要检查已发送的请求,您可以这样做:

    r = requests.post(...)
    r.request
    # PreparedRequest('POST', url, ...)
    r.request.body
    r.request.headers
    # etc.
    

    要在发送前修改请求,您可以执行以下操作:

    from requests import Request, Session
    
    s = Session()
    r = Request('POST', url, datajson_data, headers=headers)
    p = r.prepare()
    p.body = 'New body'
    p.headers = #etc.
    s.send(p, verify=pem_file)
    

    【讨论】:

    • 我正在使用 JBoss 进行服务器部署,我不能随意更改服务器端的代码。我不明白为什么相同的pem 文件在我将其包装到ssl 套接字时工作正常,但不适用于requests 模块。我相信我需要指定的唯一标头是 Content-Type 和 Accept(也许我不需要指定任何标头?),所以我删除了 Content-Length。不幸的是,这并没有改善问题。
    • 毫无疑问,您应该提供Content-TypeAccept 是可选的,但这不是问题。当你说你使用ssl socket时,你的意思是调用import ssl; ss.wrap_socket(socket, certfile=pem_file)吗?
    • 是的,但是我使用的是ca_certs参数,所以看起来像:import ssl; conn = ssl.wrap_socket(sock, ca_certs = pem_file)完整的socket配置可以找到here
    • 所以我知道有一个服务器有它自己的自签名证书。奇怪的是,使用cert='/path/to/pem_file' 不起作用,但verify='/path/to/pem_file' 起作用(没有指定cert)。你试过吗?
    • @AndrewHarasta 我用文档中解释的地方编辑了我的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-14
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    • 2013-11-22
    • 2014-12-20
    相关资源
    最近更新 更多