如何从使用OpenID的网站请求页面?

时间:2011-09-01 17:32:16

标签: python authentication openid urllib2

This question has been asked here before。对于提问者和回答者来说,接受的答案可能是显而易见的 - 但不是对我而言。我已就上述问题发表评论以获得更多精确度,但没有回应。 I also approached the meta Q&A寻求如何从他们的坟墓中提出问题的帮助,也没有得到答案。

以上问题的答案是:

  

从客户端的角度来看,OpenID登录与任何其他基于Web的登录非常相似。客户端没有定义的协议;它是一个普通的Web会话,根据您的OpenID提供程序而有所不同。出于这个原因,我怀疑是否存在任何此类库。您可能需要自己编写代码。

我已经知道如何log onto a website with Python使用Urllib2模块。但这还不足以让我猜测如何对OpenID进行身份验证。

我实际上是想获得my StackOverflow inbox in json format,我需要登录。

有人可以提供一个简短的介绍或链接到一个很好的教程如何做到这一点?

5 个答案:

答案 0 :(得分:11)

好吧,我自己对OpenID了解不多,但你的帖子(以及赏金!!)让我感兴趣。

This link告诉OpenID身份验证序列的确切流程(Atleast for v1.0。新版本为2.0)。根据我的想法,步骤将类似于

  1. 您获取stackoverflow的登录页面,该页面还将提供使用OpenID登录的选项(作为表单字段)。
  2. 你发送你的openID,这实际上是uri的一种形式,而不是用户名/电子邮件(如果是Google个人资料,则是你的个人资料ID)
  3. 然后,Stackoverflow将连接到您的ID提供商(在这种情况下为google)并向您发送重定向到Google登录页面以及您应该稍后重定向的另一个链接(假设 a
  4. 您可以按常规方式登录谷歌提供的页面(使用Python的POST方法)
  5. Google提供加密令牌(不太确定此步骤)以回复您的登录请求
  6. 您使用此令牌将新请求发送到 a
  7. Stackoverflow将使用此令牌与Google联系。如果建立了真实性,它将返回会话ID
  8. 以后对STackOverflow的请求应具有此会话ID
  9. 不知道退出!!
  10. This link讲述了OpenID中的各种响应及其含义。因此,当您的代码成为您的客户时,它可能会派上用场。

    来自维基页面的链接OpenID Explained

    编辑:使用Tamper Data Add on for Firefox,可以构建以下事件序列。

    1. 用户向SO登录页面发送请求。在表单字段中输入openID后,生成的页面会将302重定向发送到google页面。 重定向网址有很多OpenID参数(适用于Google服务器)。其中一个是return_to = https://stackoverflow.com/users/authenticate/?s=some_value
    2. 向用户显示google登录页面。在登录时,有几个302在谷歌领域重定向用户。
    3. 最后收到302,将用户重定向到< return_to' 之前指定的stackoverflow页面
    4. 在整个系列操作过程中,已经生成了许多必须正确存储的cookie
    5. 在访问SO页面(由谷歌提供302)时,SO服务器会处理您的请求,并在响应标头中发送一个字段" Set-Cookie"将名为gauth和usr的cookie与另一个302一起设置为stackoverflow.com。此步骤完成登录
    6. 您的客户只是存储cookie usr
    7. 只要您记得向任何请求发送Cookie usr,您就已登录。
    8. 您现在可以请求您的收件箱记住发送带有请求的usr cookie。
    9. 我建议您开始编写python客户端并仔细研究响应。在大多数情况下,它将是一系列302,用户干预最少(填写您的Google用户名和密码并允许网站页面除外)。

      然而,为了方便起见,您可以使用浏览器登录SO,复制所有cookie值并使用urllib2发出请求并设置cookie值。

      当然,如果您在浏览器上注销,则必须再次登录并更改python程序中的cookie值。

答案 1 :(得分:6)

我知道这很接近考古学,挖了两年的帖子,但我刚从经过验证的答案中写了一个新的增强版代码,所以我觉得在这里分享它可能很酷,因为这个问题/ answers对我来说是一个很好的帮助。

所以,这里有什么不同:

  • 它使用的新requests库是urllib2;
  • 的增强功能
  • 它支持使用google和stackexchange的openid提供程序进行身份验证。
  • 虽然打印输出较少,但阅读时间更短,更简单

这是代码:

#!/usr/bin/env python

import sys
import urllib
import requests
from BeautifulSoup import BeautifulSoup

def get_google_auth_session(username, password):
    session = requests.Session()
    google_accounts_url = 'http://accounts.google.com'
    authentication_url = 'https://accounts.google.com/ServiceLoginAuth'
    stack_overflow_url = 'http://stackoverflow.com/users/authenticate'

    r = session.get(google_accounts_url)
    dsh = BeautifulSoup(r.text).findAll(attrs={'name' : 'dsh'})[0].get('value').encode()
    auto = r.headers['X-Auto-Login']
    follow_up = urllib.unquote(urllib.unquote(auto)).split('continue=')[-1]
    galx = r.cookies['GALX']

    payload = {'continue' : follow_up,
               'followup' : follow_up,
               'dsh' : dsh,
               'GALX' : galx,
               'pstMsg' : 1,
               'dnConn' : 'https://accounts.youtube.com',
               'checkConnection' : '',
               'checkedDomains' : '',
               'timeStmp' : '',
               'secTok' : '',
               'Email' : username,
               'Passwd' : password,
               'signIn' : 'Sign in',
               'PersistentCookie' : 'yes',
               'rmShown' : 1}

    r = session.post(authentication_url, data=payload)

    if r.url != authentication_url: # XXX
        print "Logged in"
    else:
        print "login failed"
        sys.exit(1)

    payload = {'oauth_version' : '',
               'oauth_server' : '',
               'openid_username' : '',
               'openid_identifier' : ''}
    r = session.post(stack_overflow_url, data=payload)
    return session

def get_so_auth_session(email, password):
    session = requests.Session()
    r = session.get('http://stackoverflow.com/users/login')
    fkey = BeautifulSoup(r.text).findAll(attrs={'name' : 'fkey'})[0]['value']

    payload = {'openid_identifier': 'https://openid.stackexchange.com',
               'openid_username': '',
               'oauth_version': '',
               'oauth_server': '',
               'fkey': fkey,
               }
    r = session.post('http://stackoverflow.com/users/authenticate', allow_redirects=True, data=payload)
    fkey = BeautifulSoup(r.text).findAll(attrs={'name' : 'fkey'})[0]['value']
    session_name = BeautifulSoup(r.text).findAll(attrs={'name' : 'session'})[0]['value']

    payload = {'email': email,
               'password': password,
               'fkey': fkey,
               'session': session_name}

    r = session.post('https://openid.stackexchange.com/account/login/submit', data=payload)
    # check if url changed for error detection
    error = BeautifulSoup(r.text).findAll(attrs={'class' : 'error'})
    if len(error) != 0:
        print "ERROR:", error[0].text
        sys.exit(1)
    return session

if __name__ == "__main__":
    prov = raw_input('Choose your openid provider [1 for StackOverflow, 2 for Google]: ')
    name = raw_input('Enter your OpenID address: ')
    pswd = getpass('Enter your password: ')
    if '1' in prov:
        so = get_so_auth_session(name, pswd)
    elif '2' in prov:
        so = get_google_auth_session(name, pswd)
    else:
        print "Error no openid provider given"

    r = so.get('http://stackoverflow.com/inbox/genuwine')
    print r.json()

代码也可以github gist

的形式提供

HTH

答案 2 :(得分:3)

此答案总结了其他人在下面所说的内容,尤其是RedBaron,并添加了一个我用来使用Google帐户访问StackOverflow收件箱的方法。

使用Firefox的Tamper Data开发人员工具并登录到StackOVerflow,可以看到OpenID以这种方式工作:

  1. StackOverflow从发布数据中定义的给定服务(此处为Google)请求身份验证;
  2. Google帐户接管并检查已存在的Cookie作为身份验证的证据;
  3. 如果未找到Cookie,Google会请求身份验证并设置Cookie;
  4. 设置cookie后,StackOverflow会确认用户身份验证。
  5. 以上总结了这个过程,实际上更复杂,因为确实发生了许多重定向和cookie交换。

    因为以编程方式再现相同的过程在某种程度上证明是困难的(这可能只是我的文盲),特别是试图搜索URL以使用所有区域设置等来调用。我选择首先登录Google帐户,获得良好应该使用cookie,然后登录到Stackoverflow,它将使用cookie进行身份验证。

    只需使用以下Python模块即可完成:urllib,urllib2,cookielib和BeautifulSoup。

    这是(简化的)代码,它并不完美,但它可以解决问题。可以在Github上找到扩展版本。

    #!/usr/bin/env python
    
    import urllib
    import urllib2
    import cookielib
    from BeautifulSoup import BeautifulSoup
    from getpass import getpass
    
    # Define URLs
    google_accounts_url = 'http://accounts.google.com'
    authentication_url = 'https://accounts.google.com/ServiceLoginAuth'
    stack_overflow_url = 'https://stackoverflow.com/users/authenticate'
    genuwine_url = 'https://stackoverflow.com/inbox/genuwine'
    
    # Build opener
    jar = cookielib.CookieJar()
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(jar))
    
    def request_url(request):    
        '''
            Requests given URL.
        '''     
        try:
            response = opener.open(request)
        except:
            raise
        return response
    
    
    def authenticate(username='', password=''):        
        '''
            Authenticates to Google Accounts using user-provided username and password,
            then authenticates to StackOverflow.
        '''
        # Build up headers
        user_agent = 'Mozilla/5.0 (Ubuntu; X11; Linux i686; rv:8.0) Gecko/20100101 Firefox/8.0'
        headers = {'User-Agent' : user_agent}
    
        # Set Data to None
        data = None
    
        # Build up URL request with headers and data    
        request = urllib2.Request(google_accounts_url, data, headers)
        response = request_url(request)
    
        # Build up POST data for authentication
        html = response.read()
        dsh = BeautifulSoup(html).findAll(attrs={'name' : 'dsh'})[0].get('value').encode()
    
        auto = response.headers.getheader('X-Auto-Login')
    
        follow_up = urllib.unquote(urllib.unquote(auto)).split('continue=')[-1]
    
        galx = jar._cookies['accounts.google.com']['/']['GALX'].value
    
        values = {'continue' : follow_up,
                  'followup' : follow_up,
                  'dsh' : dsh,
                  'GALX' : galx,
                  'pstMsg' : 1,
                  'dnConn' : 'https://accounts.youtube.com',
                  'checkConnection' : '',
                  'checkedDomains' : '',
                  'timeStmp' : '',
                  'secTok' : '',
                  'Email' : username,
                  'Passwd' : password,
                  'signIn' : 'Sign in',
                  'PersistentCookie' : 'yes',
                  'rmShown' : 1}
    
        data = urllib.urlencode(values)
    
        # Build up URL for authentication
        request = urllib2.Request(authentication_url, data, headers)
        response = request_url(request)
    
        # Check if logged in
        if response.url != request._Request__original:
            print '\n Logged in :)\n'
        else:
            print '\n Log in failed :(\n'
    
        # Build OpenID Data    
        values = {'oauth_version' : '',
                  'oauth_server' : '',
                  'openid_username' : '',
                  'openid_identifier' : 'https://www.google.com/accounts/o8/id'}
    
        data = urllib.urlencode(values)
    
        # Build up URL for OpenID authetication
        request = urllib2.Request(stack_overflow_url, data, headers)
        response = request_url(request)
    
        # Retrieve Genuwine
        data = None
        request = urllib2.Request(genuwine_url, data, headers)
        response = request_url(request)
        print response.read()
    
    
    if __name__ == '__main__':
        username = raw_input('Enter your Gmail address: ')
        password = getpass('Enter your password: ')
        authenticate(username, password)
    

答案 3 :(得分:0)

您需要在任何“登录”页面上实现cookie,在Python中使用cookiejar。例如:

jar = cookielib.CookieJar()
myopener = urllib2.build_opener(urllib2.HTTPCookieProcessor(jar))
#myopener now supports cookies.
....

答案 4 :(得分:0)

我制作了一个简单的脚本,使用Mozilla Firefox cookie登录到stackoverflow.com。它不是完全自动化的,因为你需要手动登录,但这是我设法做到的。

Scipt对于FF的最新版本是实际的(我使用的是8.0.1),但你需要获得最新的sqlite dll,因为python 2.7附带的默认版本无法打开DB。你可以在这里得到它:http://www.sqlite.org/sqlite-dll-win32-x86-3070900.zip

import urllib2
import webbrowser
import cookielib
import os
import sqlite3
import re
from time import sleep

#login in Firefox. Must be default browser. In other cases log in manually
webbrowser.open_new('http://stackoverflow.com/users/login')

#wait for user to log in
sleep(60)

#Process profiles.ini to get path to cookies.sqlite
profile = open(os.path.join(os.environ['APPDATA'],'Mozilla','Firefox','profiles.ini'), 'r').read()

COOKIE_DB = os.path.join(os.environ['APPDATA'],'Mozilla','Firefox','Profiles',re.findall('Profiles/(.*)\n',profile)[0],'cookies.sqlite')
CONTENTS = "host, path, isSecure, expiry, name, value"

#extract cookies for specific host
def get_cookies(host):
    cj = cookielib.LWPCookieJar()   
    con = sqlite3.connect(COOKIE_DB)
    cur = con.cursor()
    sql = "SELECT {c} FROM moz_cookies WHERE host LIKE '%{h}%'".format(c=CONTENTS, h=host)
    cur.execute(sql)
    for item in cur.fetchall():
        c = cookielib.Cookie(0, item[4], item[5],
            None, False,
            item[0], item[0].startswith('.'), item[0].startswith('.'),
            item[1], False,
            item[2],
            item[3], item[3]=="",
            None, None, {})
        cj.set_cookie(c)
    return cj

host = 'stackoverflow'

cj = get_cookies(host)

opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))

response = opener.open('http://stackoverflow.com').read()

# if username in response - Auth successful
if 'Stanislav Golovanov' in response:
    print 'Auth successful'