25

我正在尝试通过 python 登录我大学的服务器,但我完全不确定如何生成适当的 HTTP POST、创建密钥和证书以及我可能不熟悉的过程的其他部分需要符合 SAML 规范。我可以很好地使用浏览器登录,但我希望能够使用 python 登录和访问服务器中的其他内容。

供参考,这里是网站

我尝试使用 mechanize 登录(选择表单、填充字段、通过 mechanize.Broswer.submit() 等单击提交按钮控件)无济于事;登录站点每次都会被吐出。

在这一点上,我愿意以最适合该任务的任何语言实现解决方案。基本上,我想以编程方式登录到经过 SAML 身份验证的服务器。

4

10 回答 10

36

基本上,您必须了解的是 SAML 身份验证过程背后的工作流程。不幸的是,似乎没有 PDF 可以很好地帮助您了解浏览器在访问受 SAML 保护的网站时会执行哪些操作。

也许你应该看看这样的东西:http: //www.docstoc.com/docs/33849977/Workflow-to-Use-Shibboleth-Authentication-to-Sign 显然是这样的:http://en.wikipedia .org/wiki/Security_Assertion_Markup_Language。特别要注意这个方案:

在此处输入图像描述

当我试图理解 SAML 的工作方式时,我所做的就是写下(是的!写在纸上)浏览器从头到尾执行的所有步骤。我使用了 Opera,将其设置为不允许自动重定向(300、301、302 响应代码等),也不允许启用 Javascript。然后我写下了服务器发送给我的所有 cookie,做了什么,出于什么原因。

也许这是太多的努力,但通过这种方式,我能够用 Java 编写一个适合这项工作的库,而且速度也快得令人难以置信。也许有一天我会公开它......

您应该了解的是,在 SAML 登录中,有两个参与者在扮演:IDP(身份提供者)和 SP(服务提供者)。

A. 第一步:用户代理向 SP 请求资源

我很确定您从另一个页面点击“访问受保护的网站”之类的内容到达了您在问题中引用的链接。如果您多加注意,您会注意到您所访问的链接不是显示身份验证表单的链接。这是因为点击从 IDP 到 SP 的链接是 SAML 的一个步骤。第一步,其实。它允许 IDP 定义您是谁,以及您尝试访问其资源的原因。因此,基本上您需要做的是向您所关注的链接发出请求以访问 Web 表单,并获取它将设置的 cookie。你不会看到一个 SAMLRequest 字符串,编码到链接后面的 302 重定向中,

我认为这就是你不能将整个过程机械化的原因。您只需连接到表单,无需进行身份识别!

B. 第二步:填写表格并提交

这很容易。请小心!现在设置的cookie与上面的 cookie 不同。您现在连接到一个完全不同的网站。这就是使用 SAML 的原因:不同的网站,相同的凭据。因此,您可能希望将这些由成功登录提供的身份验证 cookie 存储到不同的变量中。IDP 现在将向您发送一个响应(在 SAMLRequest 之后):SAMLResponse。您必须检测它获取登录结束的网页的源代码。事实上,这个页面是一个包含响应的大表单,在页面加载时,JS 中的一些代码会自动提交它。您必须获取页面的源代码,解析它以消除所有 HTML 无用的内容,并获取 SAMLResponse(加密)。

C. 第三步:将响应发回给 SP

现在您已准备好结束该过程。您必须(通过 POST,因为您正在模拟表单)将上一步中获得的 SAMLResponse 发送到 SP。通过这种方式,它将提供访问您想要访问的受保护内容所需的 cookie。

Aaaaand,你完成了!

同样,我认为您必须做的最宝贵的事情是使用 Opera 并分析 SAML 所做的所有重定向。然后,在您的代码中复制它们。这并不难,只要记住 IDP 与 SP 完全不同。

于 2013-05-20T09:24:40.757 回答
10

带有无头 PhantomJS webkit 的 Selenium 将是您登录 Shibboleth 的最佳选择,因为它为您处理 cookie 甚至 Javascript。

安装:

$ pip install selenium
$ brew install phantomjs

from selenium import webdriver
from selenium.webdriver.support.ui import Select # for <SELECT> HTML form

driver = webdriver.PhantomJS()
# On Windows, use: webdriver.PhantomJS('C:\phantomjs-1.9.7-windows\phantomjs.exe')

# Service selection
# Here I had to select my school among others 
driver.get("http://ent.unr-runn.fr/uPortal/")
select = Select(driver.find_element_by_name('user_idp'))
select.select_by_visible_text('ENSICAEN')
driver.find_element_by_id('IdPList').submit()

# Login page (https://cas.ensicaen.fr/cas/login?service=https%3A%2F%2Fshibboleth.ensicaen.fr%2Fidp%2FAuthn%2FRemoteUser)
# Fill the login form and submit it
driver.find_element_by_id('username').send_keys("myusername")
driver.find_element_by_id('password').send_keys("mypassword")
driver.find_element_by_id('fm1').submit()

# Now connected to the home page
# Click on 3 links in order to reach the page I want to scrape
driver.find_element_by_id('tabLink_u1240l1s214').click()
driver.find_element_by_id('formMenu:linknotes1').click()
driver.find_element_by_id('_id137Pluto_108_u1240l1n228_50520_:tabledip:0:_id158Pluto_108_u1240l1n228_50520_').click()

# Select and print an interesting element by its ID
page = driver.find_element_by_id('_id111Pluto_108_u1240l1n228_50520_:tableel:tbody_element')
print page.text

笔记:

  • 在开发过程中,使用 Firefox 预览你正在做的事情driver = webdriver.Firefox()
  • 该脚本按原样提供并带有相应的链接,因此您可以将每一行代码与页面的实际源代码进行比较(至少在登录之前)。
于 2014-05-29T09:22:57.097 回答
6

扩展上面 Stéphane Bruckert 的答案,一旦您使用 Selenium 获取身份验证 cookie,您仍然可以切换到请求,如果您愿意:

import requests
cook = {i['name']: i['value'] for i in driver.get_cookies()}
driver.quit()
r = requests.get("https://protected.ac.uk", cookies=cook)
于 2014-09-10T16:24:14.640 回答
2

您可以在此处找到有关 Shibboleth 身份验证过程的更详细说明。

于 2013-11-12T18:08:01.740 回答
2

我按照接受的答案编写了这段代码。这在两个单独的项目中对我有用

import mechanize
from bs4 import BeautifulSoup
import urllib2
import cookielib


cj = cookielib.CookieJar()
br = mechanize.Browser()
br.set_handle_robots(False)
br.set_cookiejar(cj)

br.set_handle_equiv(True)
br.set_handle_gzip(True)
br.set_handle_redirect(True)
br.set_handle_refresh(False)
br.set_handle_referer(True)
br.set_handle_robots(False)

br.set_handle_refresh(mechanize._http.HTTPRefreshProcessor(), max_time=1)

br.addheaders = [('User-agent', 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.1) Gecko/2008071615 Fedora/3.0.1-1.fc9 Firefox/3.0.1')]


br.open("The URL goes here")

br.select_form(nr=0)

br.form['username'] = 'Login Username'
br.form['password'] = 'Login Password'
br.submit()

br.select_form(nr=0)
br.submit()

response = br.response().read()
print response
于 2019-10-28T21:15:50.710 回答
2

虽然已经回答,但希望这对某人有所帮助。我有一项从 SAML 网站下载文件的任务,并从 Stéphane Bruckert 的回答中获得了帮助。

如果使用无头,则需要在登录所需的重定向间隔内指定等待时间。浏览器登录后,我使用其中的 cookie 并将其与请求模块一起使用以下载文件 -从此获得帮助

这就是我的代码的样子-

from selenium import webdriver
from selenium.webdriver.chrome.options import Options  #imports

things_to_download= [a,b,c,d,e,f]     #The values changing in the url
options = Options()
options.headless = False
driver = webdriver.Chrome('D:/chromedriver.exe', options=options)
driver.get('https://website.to.downloadfrom.com/')
driver.find_element_by_id('username').send_keys("Your_username") #the ID would be different for different website/forms
driver.find_element_by_id('password').send_keys("Your_password")
driver.find_element_by_id('logOnForm').submit()
session = requests.Session()
cookies = driver.get_cookies()
for things in things_to_download:    
    for cookie in cookies: 
        session.cookies.set(cookie['name'], cookie['value'])
    response = session.get('https://website.to.downloadfrom.com/bla/blabla/' + str(things_to_download))
    with open('Downloaded_stuff/'+str(things_to_download)+'.pdf', 'wb') as f:
        f.write(response.content)            # saving the file
driver.close()
于 2019-01-08T08:33:22.990 回答
1

我编写了一个简单的 Python 脚本,可以登录到 Shibbolized 页面。

首先,我在 Firefox 中使用 Live HTTP Headers 来观察我所针对的特定 Shibbolized 页面的重定向。

urllib.request然后我使用(在 Python 3.4 中,但urllib2在 Python 2.x 中似乎具有相同的功能)编写了一个简单的脚本。我发现默认的重定向跟踪urllib.request符合我的目的,但是我发现子类很好,urllib.request.HTTPRedirectHandler并且在这个子类(类ShibRedirectHandler)中为所有 http_error_302 事件添加一个处理程序。

在这个子类中,我只是打印出参数的值(用于调试目的);请注意,为了使用默认重定向跟随,您需要结束处理程序return HTTPRedirectHandler.http_error_302(self, args...)(即调用基类 http_errror_302 处理程序。)

urllib使用 Shibbolized Authentication最重要的组件是创建OpenerDirector添加了 Cookie 处理的组件。OpenerDirector您使用以下内容构建:

cookieprocessor = urllib.request.HTTPCookieProcessor()
opener = urllib.request.build_opener(ShibRedirectHandler, cookieprocessor)
response = opener.open("https://shib.page.org")

这是一个完整的脚本,可以帮助您入门(您需要更改我提供的一些模拟 URL,并输入有效的用户名和密码)。这使用 Python 3 类;为了在 Python2 中进行这项工作,请将 urllib.request 替换为 urllib2 并将 urlib.parse 替换为 urlparse:

import urllib.request
import urllib.parse

#Subclass of HTTPRedirectHandler. Does not do much, but is very
#verbose. prints out all the redirects. Compaire with what you see
#from looking at your browsers redirects (using live HTTP Headers or similar)
class ShibRedirectHandler (urllib.request.HTTPRedirectHandler):
    def http_error_302(self, req, fp, code, msg, headers):
        print (req)
        print (fp.geturl())
        print (code)
        print (msg)
        print (headers)
        #without this return (passing parameters onto baseclass) 
        #redirect following will not happen automatically for you.
        return urllib.request.HTTPRedirectHandler.http_error_302(self,
                                                          req,
                                                          fp,
                                                          code,
                                                          msg,
                                                          headers)

cookieprocessor = urllib.request.HTTPCookieProcessor()
opener = urllib.request.build_opener(ShibRedirectHandler, cookieprocessor)

#Edit: should be the URL of the site/page you want to load that is protected with Shibboleth
(opener.open("https://shibbolized.site.example").read())

#Inspect the page source of the Shibboleth login form; find the input names for the username
#and password, and edit according to the dictionary keys here to match your input names
loginData = urllib.parse.urlencode({'username':'<your-username>', 'password':'<your-password>'})
bLoginData = loginData.encode('ascii')

#By looking at the source of your Shib login form, find the URL the form action posts back to
#hard code this URL in the mock URL presented below.
#Make sure you include the URL, port number and path
response = opener.open("https://test-idp.server.example", bLoginData)
#See what you got.
print (response.read())
于 2014-05-22T13:03:47.100 回答
1

Mechanize 也可以完成这项工作,只是它不处理 Javascript。身份验证成功,但一旦在主页上,我无法加载这样的链接:

<a href="#" id="formMenu:linknotes1"
   onclick="return oamSubmitForm('formMenu','formMenu:linknotes1');">

如果您需要 Javascript,最好将Selenium 与 PhantomJS 一起使用。否则,我希望你能从这个脚本中找到灵感:

#!/usr/bin/env python
#coding: utf8
import sys, logging
import mechanize
import cookielib
from BeautifulSoup import BeautifulSoup
import html2text

br = mechanize.Browser() # Browser
cj = cookielib.LWPCookieJar() # Cookie Jar
br.set_cookiejar(cj) 

# Browser options
br.set_handle_equiv(True)
br.set_handle_gzip(True)
br.set_handle_redirect(True)
br.set_handle_referer(True)
br.set_handle_robots(False)

# Follows refresh 0 but not hangs on refresh > 0
br.set_handle_refresh(mechanize._http.HTTPRefreshProcessor(), max_time=1)

# User-Agent
br.addheaders = [('User-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36')]

br.open('https://ent.unr-runn.fr/uPortal/')
br.select_form(nr=0)
br.submit()

br.select_form(nr=0)
br.form['username'] = 'myusername'
br.form['password'] = 'mypassword'
br.submit()

br.select_form(nr=0)
br.submit()

rs = br.open('https://ent.unr-runn.fr/uPortal/f/u1240l1s214/p/esup-mondossierweb.u1240l1n228/max/render.uP?pP_org.apache.myfaces.portlet.MyFacesGenericPortlet.VIEW_ID=%2Fstylesheets%2Fetu%2Fdetailnotes.xhtml')

# Eventually comparing the cookies with those on Live HTTP Header: 
print "Cookies:"
for cookie in cj:
    print cookie

# Displaying page information
print rs.read()
print rs.geturl()
print rs.info();

# And that last line didn't work
rs = br.follow_link(id="formMenu:linknotes1", nr=0)
于 2014-05-29T09:45:11.763 回答
1

如果一切都失败了,我建议在“headfull”模式下使用 Selenium 的 webdriver(即会打开一个浏览器窗口,允许输入用户名、密码和任何其他必要的登录信息),这样可以轻松访问目标网站即使您的表单比标准的“用户名”和“密码”组合更复杂,并且您不确定如何填写其他答案中提到的 br.form 部分。

from selenium import webdriver
import time

DRIVER_PATH = r'C:/INSERT_YOUR_PATH_HERE/chromedriver.exe'
driver = webdriver.Chrome(executable_path=DRIVER_PATH)
driver.get('https://moodle.tau.ac.il/login/index.php') # This is the login screen

一旦你这样做了,你可以创建一个循环来检查你是否到达了你的目标 URL - 如果是,你就在里面!这段代码对我有用;我的目标是访问我大学的课程网站 Moodle 并自动下载所有 PDF。

targetUrl = False
timeElapsed = 0

def downloadAllPDFs():         # Or any other function you'd like, the point is that 
    print("Access Granted!")   # you now have access to the HTML. 

while not targetUrl and timeElapsed < 60:
    time.sleep(1)
    timeElapsed += 1
    if driver.current_url == r"https://moodle.tau.ac.il/my/": # The site you're trying to login to.
        downloadAllPDFs()
        targetUrl = True 
于 2020-09-24T05:37:59.320 回答
1

我的大学页面 SAML 身份验证也遇到了类似的问题。

基本思想是使用一个requests.session对象来自动处理大多数 http 重定向和 cookie 存储。但是,也有许多使用两种 javascript 的重定向,这导致使用简单请求解决方案时出现多个问题。

我最终使用fiddler来跟踪我的浏览器向大学服务器发出的每个请求,以填补我错过的重定向。它确实使这个过程变得更容易。

我的解决方案远非理想,但似乎有效。

于 2016-08-23T23:02:30.173 回答