2012-01-08 68 views
1

這個問題是posted on StackApps,但這個問題可能是一個編程問題,而不是一個認證問題,因此它可能在這裏值得一個更好的地方。如何以編程方式使用Python從客戶端OAuth流中檢索access_token?

我工作的桌面上的收件箱通知了StackOverflow上,使用與Python的API。

我第一次工作的腳本註冊上StackExchange的用戶,然後請求授權的應用程序。假設應用程序已通過用戶的Web瀏覽器交互進行授權,則應用程序應能夠使用身份驗證向API發出請求,因此它需要特定於用戶的訪問令牌。這是通過URL:https://stackexchange.com/oauth/dialog?client_id=54&scope=read_inbox&redirect_uri=https://stackexchange.com/oauth/login_success完成的。

當請求通過Web瀏覽器重定向正在發生和接入代碼權限的#後返回。 但是,當用Python(urllib2)請求這個相同的URL時,響應中不會返回散列或鍵。

爲什麼我的urllib2請求從Firefox或者w3m的提出同樣的要求不同的處理方式?我該怎麼做才能以編程方式模擬這個請求並檢索access_token

這裏是我的腳本(這是實驗性的),並記住:它假定用戶已經授權的應用程序。用下面的標頭

在Firefox篡改數據請求上述URL(如oauth_url中的代碼)::

Host=stackexchange.com 
User-Agent=Mozilla/5.0 (Ubuntu; X11; Linux i686; rv:9.0.1) Gecko/20100101 Firefox/9.0.1 
Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
Accept-Language=en-us,en;q=0.5 
Accept-Encoding=gzip, deflate 
Accept-Charset=ISO-8859-1,utf-8;q=0.7,*;q=0.7 
Connection=keep-alive 
Cookie=m=2; __qca=P0-556807911-1326066608353; __utma=27693923.1085914018.1326066609.1326066609.1326066609.1; __utmb=27693923.3.10.1326066609; __utmc=27693923; __utmz=27693923.1326066609.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); gauthed=1; ASP.NET_SessionId=nt25smfr2x1nwhr1ecmd4ok0; se-usr=t=z0FHKC6Am06B&s=pblSq0x3B0lC 

在urllib2的請求報頭

#!/usr/bin/env python 

import urllib 
import urllib2 
import cookielib 
from BeautifulSoup import BeautifulSoup 
from getpass import getpass  

# Define URLs 
parameters = [ 'client_id=54', 
       'scope=read_inbox', 
       'redirect_uri=https://stackexchange.com/oauth/login_success' 
      ] 

oauth_url = 'https://stackexchange.com/oauth/dialog?' + '&'.join(parameters) 
login_url = 'https://openid.stackexchange.com/account/login' 
submit_url = 'https://openid.stackexchange.com/account/login/submit' 
authentication_url = 'http://stackexchange.com/users/authenticate?openid_identifier=' 

# Set counter for requests: 
counter = 0 

# Build opener 
jar = cookielib.CookieJar() 
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(jar)) 

def authenticate(username='', password=''): 

    ''' 
     Authenticates to StackExchange using user-provided username and password 
    ''' 

    # 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 

    # 1. Build up URL request with headers and data  
    request = urllib2.Request(login_url, data, headers) 
    response = opener.open(request) 

    # Build up POST data for authentication 
    html = response.read() 
    fkey = BeautifulSoup(html).findAll(attrs={'name' : 'fkey'})[0].get('value').encode() 

    values = {'email' : username, 
       'password' : password, 
       'fkey' : fkey} 

    data = urllib.urlencode(values) 

    # 2. Build up URL for authentication 
    request = urllib2.Request(submit_url, data, headers) 
    response = opener.open(request) 

    # Check if logged in 
    if response.url == 'https://openid.stackexchange.com/user': 
     print ' Logged in! :) ' 
    else: 
     print ' Login failed! :(' 

    # Find user ID URL  
    html = response.read() 
    id_url = BeautifulSoup(html).findAll('code')[0].text.split('"')[-2].encode() 

    # 3. Build up URL for OpenID authentication 
    data = None 
    url = authentication_url + urllib.quote_plus(id_url) 
    request = urllib2.Request(url, data, headers) 
    response = opener.open(request) 

    # 4. Build up URL request with headers and data 
    request = urllib2.Request(oauth_url, data, headers) 
    response = opener.open(request) 

    if '#' in response.url: 
     print 'Access code provided in URL.' 
    else: 
     print 'No access code provided in URL.' 

if __name__ == '__main__': 
    username = raw_input('Enter your username: ') 
    password = getpass('Enter your password: ') 
    authenticate(username, password) 

要以下面的評論響應僅提供用戶代理值。該cookie未被明確傳遞,但在請求時cookie jar中可用se-usr

響應標頭將第一重定向:

Status=Found - 302 
Server=nginx/0.7.65 
Date=Sun, 08 Jan 2012 23:51:12 GMT 
Content-Type=text/html; charset=utf-8 
Connection=keep-alive 
Cache-Control=private 
Location=https://stackexchange.com/oauth/login_success#access_token=OYn42gZ6r3WoEX677A3BoA))&expires=86400 
Set-Cookie=se-usr=t=kkdavslJe0iq&s=pblSq0x3B0lC; expires=Sun, 08-Jul-2012 23:51:12 GMT; path=/; HttpOnly 
Content-Length=218 

然後重定向將通過與從該頭新鮮se-usr值的另一請求發生。

我不知道如何捕捉到302 urllib2的,它自己處理它(這是偉大的)。然而,看看位置標題中提供的訪問令牌是否可用,這將是很好的。

沒有什麼特別的,在過去的響應頭的,Firefox和urllib的返回類似:

Server: nginx/0.7.65 
Date: Sun, 08 Jan 2012 23:48:16 GMT 
Content-Type: text/html; charset=utf-8 
Connection: close 
Cache-Control: private 
Content-Length: 5664 

我希望我沒有提供機密信息,讓我知道如果我這樣做:d

+0

在黑暗中拍攝,但如果您複製您通過Firefox發送的標題,它是否工作?只是想,也許他們正在認識到你不是通過瀏覽器來的。順便說一句,你通過urllib2收到的迴應是什麼? http://www.voidspace.org.uk/python/articles/urllib2.shtml#headers – sgallen 2012-01-08 20:49:51

+0

感謝您的評論:請參閱編輯的回覆。 NB:我在頭文件中提供了user_agent ='Mozilla/5.0(Ubuntu; X11; Linux i686; rv:8.0)Gecko/20100101 Firefox/8.0''。 – Benjamin 2012-01-09 00:03:38

回答

2

由於urllib2處理重定向的方式,令牌不會出現。我對細節不熟悉,所以我不會在這裏詳細說明。

解決方案是在urllib2處理重定向之前捕獲302。這可以通過對urllib2.HTTPRedirectHandler進行子分類以獲得具有其標籤和標記的重定向來完成。這裏是子類的處理器的一個簡短的例子:

class MyHTTPRedirectHandler(urllib2.HTTPRedirectHandler): 
    def http_error_302(self, req, fp, code, msg, headers): 
     print "Going through 302:\n" 
     print headers 
     return urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers) 

在報頭中的location屬性將提供重定向URL全長,即,包括主題標籤和令牌:

輸出提取物:

... 
Going through 302: 

Server: nginx/0.7.65 
Date: Mon, 09 Jan 2012 20:20:11 GMT 
Content-Type: text/html; charset=utf-8 
Connection: close 
Cache-Control: private 
Location: https://stackexchange.com/oauth/login_success#access_token=K4zKd*HkKw5Opx(a8t12FA))&expires=86400 
Content-Length: 218 
... 

更多關於在StackOverflow(當然)上使用urllib2捕獲重定向。

相關問題