Python ServiceNow 身份验证 JSON Web 服务

Python authentication for ServiceNow JSON Web Service

我正在 Python 开发一个报告工具,它可以从 ServiceNow 的 JSON Web 服务获取数据。我们的 ServiceNow 实例使用普通用户 id / pw 身份验证加上 SHA-1 认证。我的问题是我无法访问 JSON Web 服务结果页面 (https://servicenowserver.com/table.do?JSONv2&sysparm_query=active=true^number=12345678) with my script to grab the data from there. I can log in with my script to the main page (https://servicenowserver.com),它进行身份验证并提供 HTTP 200,但是当我调用 JSON Web 服务页面时是给我 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

我想出了解决办法,所以我把它发布在这里。我要在这里发布的不是我的问题的确切解决方案,而是了解和检查如何跟踪身份验证的一般方法。我使用这种技术来跟踪我的登录过程。

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

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

1.) 转到您要通过的 Google Chrome 中的登录页面,等待网站加载。暂时不要登录。

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

3.) 转到 Dev Tools 的 Application 选项卡,然后单击左侧菜单栏中的 Clear storage。这将清除为此页面存储的所有数据。您可以通过清除 cookie 和其他数据在 Chrome 的设置菜单中执行相同的操作。这是清除页面上已经发生的所有历史步骤所必需的,以免造成任何混淆。

4.) 完成后,转到 Dev Tools 的 Network 选项卡,然后单击 Clear 菜单选项(在 Record 按钮旁边)。这将清除网络日志历史记录。

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

6.) 现在我们删除了所有历史数据并设置了所有内容,我们可以开始调查了。使用您的用户名和密码登录页面并保持开发者工具打开,这样您就可以看到所有网络请求。等待登录过程完成,然后开始一项一项地检查您的网络日志条目。

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

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

在我的第一个 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

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

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]

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

的“一般信息”部分看到它

还有一个重要提示。如果您的站点上有重定向,请在 requests 中使用 allow_redirects=False。通过这种方式,您可以确保您的请求不会被重定向到其他站点,并且您会得到正确的响应 Headers 信息。