【问题标题】:Python: use xmlrpc module with a server requiring authenticationPython:将 xmlrpc 模块与需要身份验证的服务器一起使用
【发布时间】:2015-09-10 11:23:22
【问题描述】:

我想使用 Python 脚本查询 XML-RPC 服务器。

第一个障碍是服务器使用身份验证,我不得不求助于 requests.Session 来克服这个问题。现在我做到了,我可以通过 RPC 查询服务器,但是我使用 相当丑的机制来做到这一点 - 粘合字符串。当然,这只会产生 XML 数据,我必须再次手动处理才能将其转换为可用的列表或字典。

我发现有一个用于 Python 的 xmlrpc 模块,它允许干净地使用服务器;但是,在使用 XML-RPC 之前,我找不到如何对自己进行身份验证。

这是我已经使用请求编写的代码:

from requests import Session

myserver_address='http://myserver.test.com/admin'
myserver_RPC=myserver_address+'?RPC2'
myserver_header={'Content-Type': "text/xml; charset=UTF-8"}
myserver_login={"srvAction":"LoginAdmin", "login":"testuser", "password":"testpassword", "Submit":"Login", "select_locale":"en"}
myserver_login2={"srvAction":"LoginOrg", "selectSection":"20", "submit":"Continue"}

method_header = '<methodCall xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions"><methodName>'
param_header = '</methodName><params>'
param_body_header = '<param><value><string>'
param_body_footer = '</string></value></param>'
method_footer = '</params></methodCall>'

def myserver_get_xml(myserver_method, myserver_method_param):
    param_body = ''

    for param in myserver_method_param:
        param_body = param_body + param_body_header + str(param) + param_body_footer

    myserver_post = method_header + myserver_method + param_header + param_body + method_footer
    page = s.post(myserver_RPC, myserver_post)
    return page.text

s = Session()
s.get(myserver_address)
s.post(myserver_address, myserver_login) 
s.post(myserver_address, myserver_login2)

s.headers.update(myserver_header)

result_xml = myserver_get_xml('myserverServer.getNetworkTree', ['31', ] )
print(result_xml)

所以问题是:

  • 如何在经过身份验证的会话中使用 xmlrpc 模块?

  • 如果这不可能,有什么好的方法可以将 XML 转换为字典(最好)或列表?

示例输出:

<?xml version="1.0" encoding="UTF-8"?><methodResponse xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions"><params><param><value><array><data><value><array><data><value>10.0.0.0/16</value><value><i4>1</i4></value><value>31</value></data></array></value><value><array><data><value>10.1.0.0/16</value><value><i4>2</i4></value><value>31</value></data></array></value></data></array></value></param></params></methodResponse>

【问题讨论】:

  • 感谢@Bernhard,我想出了如何使用组合选项:我现在导入 xmlrpc.client 而不是丑陋的 myserver_get_xml 函数,并使用xmlrpc.client.dumps 函数如下: page_test = s.post(myserver_RPC, xmlrpc.client.dumps( params=('31', ), methodname='myserverServer.getNetworkTree') )。效果很好:-)

标签: python xml python-requests rpc xml-rpc


【解决方案1】:

您需要实现自己的xmlrpclib.Transport 类,并在实例化xmlrpclib.ServerProxy 时将其指定为传输参数。请参阅https://docs.python.org/2/library/xmlrpclib.html#example-of-client-usage 上的代理传输实现示例。 您可能只需要实现和修改Transport.make_connection,它需要返回一个httplib.HTTPConnection 实例,该实例具有从您的登录操作的http 响应标头设置的Cookie 标头。您不能真正使用 Session 类来发出请求,因为它与 Transport 类方法的其余部分使用的 httplib.HTTPConnection 类不兼容。因此,为了简单起见,您可以使用 Session 实例执行登录,提取标头(特别是 cookie 标头)并使用这些标头创建 HTTPConnection。

def create_session_headers():
      s = requests.Session()
      # perform your request.Session creation with the login posts
      # return headers from the loggedin session
      return s.headers

class SessionTransport(xmlrpclib.Transport):
    def make_connection(self, host):
          # create the standard connection
          connection = super(SessionTransport, self).make_connection(host)
          session_headers = create_session_headers()
          for key, value in session_headers.items():
              connection.putheader(key, value)
          return connection

 service = xmlrpclib.ServerProxy(server_address, SessionTransport())

我不确定它是否会像这样工作,我还没有测试过。我希望它能为您提供足够的信息来尝试这种方法。

【讨论】:

  • 叫我dense,虽然听起来很不错,但我不知道该怎么做。你有没有机会至少为它写一些伪代码?我应该可以从那里拿走它。
  • 我已经用一个可能满足您需要的示例更新了答案。
  • 所以,它可能更接近 - 不幸的是,无论我尝试做什么,我都会得到 "TypeError: request() missing 1 required positional argument: 'request_body'" 。我想要做的请求是:service.system.listMethods()。我刚刚花了大约 3 个小时试图弄清楚图书馆应该如何工作,但我还不是绝地:(你知道可能出了什么问题吗?
  • 无法编辑之前的评论,但我刚刚意识到:service = xmlrpclib.ServerProxy(server_address, SessionTransport) 应该是 service = xmlrpclib.ServerProxy(server_address , SessionTransport() ) - 我花了一些时间才意识到 :D 它仍然不起作用,但我更近了一步 :D
  • 又近了一步:更改为 for key, value in session_headers.items() 我现在得到:http.client.CannotSendHeader 错误在做service.system.listMethods()。恐怕复制标头不起作用 - 我认为服务器只是跟踪 5 元组来识别登录后的连接。它需要始终保持相同的连接。我尝试跳过标题(只是 {'Accept': '*/*', 'User-Agent': 'python-requests/2.7.0 CPython/3.4.1 Windows/7', 'Connection': 'keep-alive', 'Accept-Encoding': 'gzip, deflate'})它只是说管理员没有登录。
【解决方案2】:

不久前找到了解决方案,到目前为止有效。它非常粗糙,不适合处理 XML,但因为我只需要应用程序中的一些数据,所以它可以工作。

#!/cygdrive/c/Python34/python.exe

from requests import Session
import sys
import json
import xmltodict

myserver_address='http://myserver.test.com/admin'
myserver_RPC=myserver_address+'?RPC2'
myserver_header={'Content-Type': "text/xml; charset=UTF-8"}
myserver_login={"srvAction":"LoginAdmin", "login":"testuser", "password":"testpassword", "Submit":"Login", "select_locale":"en"}
myserver_login2={"srvAction":"LoginOrg", "selectSection":"20", "submit":"Continue"}

s = Session()
s.get(myserver_address)
s.post(myserver_address, myserver_login) 
s.post(myserver_address, myserver_login2)

s.headers.update(myserver_header)

myserverMethod = '<methodCall xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions"><methodName>myserverMyViewUsage.getMyViewChildrenArray</methodName><params><param><value><int>602</int></value></param></params></methodCall>'
subnet_list = s.post(myserver_RPC, myserverMethod)
subnet_dictionary_coarse = xmltodict.parse(subnet_org_list.text)

#print( json.dumps(subnet_org_dictionary_coarse, indent=2))

for subnet_org_dictionary in subnet_org_dictionary_coarse['methodResponse']['params']['param']['value']['array']['data']['value']:
<put code here>

别搞错了,这基本上就是强行使用 XML 来获取我需要的数据;这是一个“快速且非常肮脏”的黑客攻击。

【讨论】:

    猜你喜欢
    • 2021-05-15
    • 1970-01-01
    • 2013-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-10
    • 2019-03-08
    • 1970-01-01
    相关资源
    最近更新 更多