ServiceNow JSON Web 服务的 Python 身份验证

g0m*_*m3z 4 python authentication servicenow

我正在用 Python 开发一个报告工具,它可以从 ServiceNow 的 JSON Web 服务中获取数据。我们的 ServiceNow 实例使用普通的用户 id / pw 身份验证加上 SHA-1 认证。我的问题是我无法使用脚本访问 JSON Web 服务结果页面(https://servicenowserver.com/table.do?JSONv2&sysparm_query=active=true ^number=12345678)以从那里获取数据。我可以使用我的脚本登录主页(https://servicenowserver.com),它进行身份验证并给出 HTTP 200,但是当我调用 JSON Webservice 页面时给出 HTTP 401(未经授权)。

当我通过浏览器登录 ServiceNow 并启动会话后,我可以在新选项卡上调用 JSON 服务,它会显示结果,但这不适用于我的 Python 脚本。我尝试将urllib3requests库与会话参数一起使用以保持会话打开,但它都不起作用。我认为我的脚本只是在调用主页后立即关闭会话。我也尝试过传递饼干,但没有成功。

长话短说:它可以在我的浏览器中运行,但如果我使用 Python 脚本则不行。

您知道我应该如何进行身份验证才能获取 JSON 结果吗?或者至少如果有人可以指导我如何获得更详细的调试?

您可以在下面找到我尝试过的解决方案之一:

import requests

s = requests.session()
s.auth = ('user', 'password')
s.verify = 'sn.cer'

r = s.get('https://servicenowserver.com', verify=True)
print (r) # This gives HTTP 200

r2 = s.get ('https://servicenowserver.com/table.do?JSONv2&sysparm_query=active=true^number=12345678', verify=True, cookies=s.cookies)
print (r2) # This gives HTTP 401
Run Code Online (Sandbox Code Playgroud)

g0m*_*m3z 5

我可以设法找出解决方案,因此我将其发布在这里。我要在这里发布的并不是我的问题的确切解决方案,而是理解和检查如何跟踪身份验证的通用方法。我使用这种技术来跟踪我的案例中的登录过程。

就我而言,ServiceNow 使用基于 cookie 的身份验证并在 4 个页面之间来回传递信息。第一个页面生成一个名为 NSC 的 ID,并将其作为 cookie 传递到第二个页面,以生成另一个名为 SMSESSION ID 的 ID,然后将其与 cookie 中的 NSC ID 一起传递到第三个页面以生成最终的 JSESSION ID。最后,该过程将所有先前生成的 3 个 ID 通过 cookie 传递到登录页面以验证会话。

我使用谷歌开发者工具来解决这个问题。我建议您执行以下操作。

1.) 转至您想要通过的 Google Chrome 登录页面,并等待网站加载。还没有登录。

2.) 打开开发人员工具(右键单击,检查元素菜单选项)。如果您熟悉其他浏览器的开发功能也没关系。

3.) 转到开发工具的“应用程序”选项卡,然后单击左侧菜单栏上的“清除存储”。这将清除为此页面存储的所有数据。您可以在 Chrome 的设置菜单中通过清除 cookie 和其他数据来执行相同的操作。这需要清除页面上已经发生的所有历史步骤,以免造成任何混乱。

4.) 完成后,转到开发工具的“网络”选项卡,然后单击“清除”菜单选项(“记录”按钮旁边)。这将清除网络日志历史记录。

5.) 下一步,勾选“网络”选项卡上的“保留日志”复选框。这将使我们即使在任何重定向的情况下也能跟踪每个步骤。如果您不勾选此选项,一旦您的登录页面将您重定向到其他地方,您将丢失所有数据,因为它会清除网络日志。

6.) 现在,当我们删除所有历史数据并设置好一切后,我们就可以开始调查了。使用您的用户 ID 和密码登录该页面,并保持开发人员工具打开,以便您可以查看所有网络请求。等待登录过程完成,然后开始一一检查您的网络日志条目。

7.) 您将看到一些 GET 和 POST 请求。这是您的登录流程。双击打开第一个。它将向您显示按部分组织的信息,例如(常规、响应标头、请求标头、查询参数、表单数据等)。这是网络服务器和客户端(您的机器)之间发生的信息交换。您需要在脚本中模拟相同的内容。这意味着您在请求标头部分中看到的任何内容,都需要在脚本中传递完全相同的内容。这样您将收到完全相同的响应标头,并且您可以从那里获取继续前进所需的所有信息。

让我向您展示并举例说明。

在我的第一个 POST 请求中,我可以在网络日志中看到以下内容:

General
Request URL:https://mysnserver.net/siteminderagent/forms/dssologinprod.fcc?TYPE=33554433&REALMOID=06-0cffd45f-7ca7-106f-bbab-84fb3af10000&GUID=&SMAUTHREASON=0&METHOD=GET&SMAGENTNAME=-SM-28THtkr3KQi%2fJmb193GjY0nVjpKo6ULc%2fJNV5hRyjzC17qWZfgyVPkR%2f7EAWoDVu3Gd3y3kTm3N2p0B8KVp0Hixjin0ZsDZ3&TARGET=-SM-%2f
Request Method:POST
Status Code:302 Found
Remote Address:1.1.1.196:443

Response Headers
Cache-Control:no-store
Connection:Keep-Alive
Content-Length:1541
Content-Type:text/html; charset=iso-8859-1
Date:Wed, 21 Sep 2016 19:11:46 GMT
Keep-Alive:timeout=5, max=496
Location:https://anothersite.com/SmMakeCookie.ccc?SMSESSION=-SM-w0Gp2DpiPEG&PERSIST=0&TARGET=-SM-https%3a%2f%2fservicemanagement%2net%2f
Set-Cookie:SMSESSION=w0Gp2DpiPEGPrLepzXds9qUTVER/Xl75WO36n37IxRpLaE6dwQPwN2+iaNn4rQZODb+65k2Gy9fggnKU04I7rSU6; path=/; domain=.mysnserver.net; secure
Set-Cookie:SMIDENTITY=EoIkGNtD3Y+FBWumdJuml3J78o61Qtc07b73XmqEeze; path=/; domain=.mysnserver.net; secure
Set-Cookie:NSC_1.1.1.196-443-C72169=ffffffffaaa3746145525d5f4f58455e445a4a4253a5;expires=Wed, 21-Sep-2016 21:11:47 GMT;path=/;secure;httponly
Set-Cookie:SMTRYNO=; expires=Fri, 25 Mar 2016 19:11:46 GMT; path=/; domain=.mysnserver.net; secure

Request Headers
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate, br
Accept-Language:en-US,en;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
Content-Length:238
Content-Type:application/x-www-form-urlencoded
Host:mysnserver.net
Origin:https://mysnserver.net
Referer:https://anothersite.net/forms/dssologinprod.fcc?TYPE=33554433&REALMOID=06-0cffd45f-7ca7-106f-bbab-84fb3af10000&GUID=&SMAUTHREASON=0&METHOD=GET&SMAGENTNAME=-SM-28THtkr3KQi%2fJmb193GjY0nVjpKo6ULc%2fJNV5hRyjzC17qWZfgyVPkR%2f7EAWoDVu3Gd3y3kTm3N2p0B8KVp0Hixjin0ZsDZ3&TARGET=-SM-%2f
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36

Query String Parameters
TYPE:33554433
REALMOID:06-0cffd45f-7ca7-106f-bbab-84fb3af10000
GUID:
SMAUTHREASON:0
METHOD:GET
SMAGENTNAME:-SM-28THtkr3KQi/Jmb193GjY0nVjpKo6ULc/JNV5hRyjzC17qWZfgyVPkR/7EAWoDVu3Gd3y3kTm3N2p0B8KVp0Hixjin0ZsDZ3
TARGET:-SM-/

Form Data
SMENC:ISO-8859-1
SMLOCALE:US-EN
target:/
smquerydata:
smauthreason:0
smagentname:28THtkr3KQi/Jmb193GjY0nVjpKo6ULc/JNV5hRyjzC17qWZfgyVPkR/7EAWoDVu3Gd3y3kTm3N2p0B8KVp0Hixjin0ZsDZ3
postpreservationdata:
USER:my_userid
PASSWORD:my_password
Run Code Online (Sandbox Code Playgroud)

无论您在请求标头部分中看到什么,都需要将其传递到第一个 URL 以获取响应标头信息。如果您在响应标头中看到我收到了服务器提供的多个 ID。这意味着我需要在 Python 中准备第一个请求,以传递与请求标头中完全相同的信息。像这样:

auth_url1 = 'https://mysnserver.net/siteminderagent/forms/dssologinprod.fcc?TYPE=33554433&REALMOID=06-0cffd45f-7ca7-106f-bbab-84fb3af10000&GUID=&SMAUTHREASON=0&METHOD=GET&SMAGENTNAME=-SM-28THtkr3KQi%2fJmb193GjY0nVjpKo6ULc%2fJNV5hRyjzC17qWZfgyVPkR%2f7EAWoDVu3Gd3y3kTm3N2p0B8KVp0Hixjin0ZsDZ3&TARGET=-SM-%2f'

# Initiating session
s = requests.session()

request_header_1 = {
    'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
    'Accept-Encoding':'gzip, deflate, br',
    'Accept-Language':'en-US,en;q=0.8',
    'Cache-Control':'max-age=0',
    'Connection':'keep-alive',
    'Content-Length':'238',
    'Content-Type':'application/x-www-form-urlencoded',
    'Host':'mysnserver.net',
    'Origin':'https://mysnserver.net',
    'Referer':'https:///anothersite.net/forms/dssologinprod.fcc?TYPE=33554433&REALMOID=06-0cffd45f-7ca7-106f-bbab-84fb3af10000&GUID=&SMAUTHREASON=0&METHOD=GET&SMAGENTNAME=-SM-28THtkr3KQi%2fJmb193GjY0nVjpKo6ULc%2fJNV5hRyjzC17qWZfgyVPkR%2f7EAWoDVu3Gd3y3kTm3N2p0B8KVp0Hixjin0ZsDZ3&TARGET=-SM-%2f',
    'Upgrade-Insecure-Requests':'1',
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36'
}

form_data_1 = {
    'SMENC':'ISO-8859-1',
    'SMLOCALE':'US-EN',
    'target':'/',
    'smquerydata':'',
    'smauthreason':'0',
    'smagentname':'28THtkr3KQi/Jmb193GjY0nVjpKo6ULc/JNV5hRyjzC17qWZfgyVPkR/7EAWoDVu3Gd3y3kTm3N2p0B8KVp0Hixjin0ZsDZ3',
    'postpreservationdata':'',
    'USER':'my_userid', #<----- Put your user ID here
    'PASSWORD':'my_password' #<----- Put your password here
}
r = s.post(auth_url1, headers=request_header_1, data=form_data_1, verify=False, allow_redirects=False)

# Get NSC ID from the response header which needs to be passed over in the 3rd request
nsc_id = r.cookies.keys()[2] + "=" + r.cookies.values()[2]
Run Code Online (Sandbox Code Playgroud)

就是这样。如果您有更多重定向,则需要遵循相同的过程,直到您通过最后一页并且您的会话进行身份验证。此后,您可以使用收集的 cookie 信息来验证您即将发出的所有请求。正如您所看到的,我已经使用s = requests.session()命令启动了一个会话,我可以使用它来提交所有请求,而无需传递所有请求的用户 ID 和密码。当您需要发送 GET 和需要发送 POST 请求时请务必小心。您可以在标题的“一般信息”部分中看到这一点。

还有一个重要的注意事项。如果您的网站上有重定向,请allow_redirects=False在您的网站中使用。requests通过这种方式,您可以确保您的请求不会被重定向到其他站点,并返回正确的响应标头信息。