2011-09-01 34 views
16

This question has been asked here before. Câu trả lời được chấp nhận có lẽ là hiển nhiên đối với cả người hỏi lẫn người trả lời --- nhưng không phải với tôi. Tôi đã nhận xét về câu hỏi trên để có thêm nhiều biện pháp, nhưng không có phản hồi. I also approached the meta Q&A để được giúp đỡ về cách mang lại câu hỏi từ ngôi mộ của họ, và cũng không có câu trả lời.Làm cách nào để yêu cầu các trang từ trang web sử dụng OpenID?

Câu trả lời cho câu hỏi ở đây trên là:

Từ quan điểm của khách hàng, một đăng nhập OpenID là rất giống với bất kỳ đăng nhập dựa trên web khác. Không có một giao thức xác định cho khách hàng; nó là một phiên web thông thường thay đổi dựa trên nhà cung cấp OpenID của bạn. Vì lý do này, tôi nghi ngờ rằng bất kỳ thư viện như vậy tồn tại. Có thể bạn sẽ phải tự viết mã.

Tôi biết cách log onto a website with Python đã có, sử dụng mô-đun Urllib2. Nhưng đó là không đủ để tôi đoán làm thế nào để xác thực với một OpenID.

Tôi thực sự cố gắng để có được my StackOverflow inbox in json format, mà tôi cần phải đăng nhập.

Có thể ai đó cung cấp một giới thiệu ngắn hoặc một liên kết đến một hướng dẫn tốt đẹp về cách để làm điều đó?

+0

PS: Tôi đã gắn cờ bài đăng này đã dành cho sự chú ý của người kiểm duyệt đối với việc cố ý sao chép. – Benjamin

Trả lời

3

Câu trả lời này tóm tắt những gì người khác đã nói dưới đây, đặc biệt là RedBaron, cộng thêm một phương pháp tôi đã sử dụng để có được StackOverflow Inbox sử dụng Tài khoản Google.

Sử dụng công cụ nhà phát triển Tamper dữ liệu của Firefox và đăng nhập vào Stackoverflow, người ta có thể thấy rằng OpenID làm việc theo cách này:

  1. StackOverflow yêu cầu xác thực từ một dịch vụ nhất định (ở đây của Google), được định nghĩa trong posted dữ liệu;
  2. Tài khoản Google tiếp quản và kiểm tra cookie hiện có làm bằng chứng xác thực;
  3. Nếu không tìm thấy cookie, Google yêu cầu xác thực và đặt cookie;
  4. Khi cookie được đặt, StackOverflow xác nhận xác thực của người dùng.

Tổng hợp ở trên, quá trình này thực tế phức tạp hơn vì nhiều chuyển hướng và trao đổi cookie thực sự xảy ra.

Vì sao chép cùng một quy trình được chứng minh bằng cách nào đó khó khăn (và đó có thể chỉ là mù chữ của tôi), đặc biệt là tìm kiếm các URL để gọi với tất cả các đặc điểm địa phương, v.v. Tôi đã chọn đăng nhập vào Tài khoản Google trước, cũng xứng đáng cookie và sau đó đăng nhập vào Stackoverflow, mà sẽ sử dụng cookie để xác thực.

Điều này được thực hiện đơn giản bằng cách sử dụng các mô-đun Python sau: urllib, urllib2, cookielib và BeautifulSoup.

Đây là mã (đã được đơn giản hóa), nó không hoàn hảo, nhưng nó thực hiện thủ thuật. Phiên bản mở rộng có thể được tìm thấy trên 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) 
+0

Ugh, tại sao HTMLParser và không giống như BeautifulSoup? – ThiefMaster

+0

@ThiefMaster: nếu bạn nghĩ rằng có một lý do chính đáng tại sao BeautifulSoup tốt hơn HTMLParser cho mục đích này, sau đó vui lòng giải thích và sửa đổi câu trả lời. – Benjamin

+0

Hai cuộc gọi hàm đơn giản so với lớp con và 6 mức thụt đầu dòng – ThiefMaster

0

Bạn cần triển khai cookie trên bất kỳ trang "đăng nhập" nào, bằng Python bạn sử dụng cookiejar. Ví dụ:

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

Tôi cũng không biết nhiều về OpenID nhưng bài đăng của bạn (và tiền thưởng !!) khiến tôi quan tâm.

This link cho biết luồng chính xác của chuỗi xác thực OpenID (Atleast cho v1.0. Phiên bản mới là 2.0). Từ những gì tôi có thể thực hiện, các bước sẽ là một cái gì đó như:

  1. Bạn tìm nạp trang đăng nhập của luồng ngăn xếp cũng sẽ cung cấp tùy chọn đăng nhập bằng OpenID (Là trường biểu mẫu).
  2. Bạn gửi ur openID thực sự là một dạng uri và KHÔNG tên người dùng/email (Nếu đó là tiểu sử trên Google thì đó là ID hồ sơ của bạn)
  3. Stackoverflow sau đó sẽ kết nối với nhà cung cấp ID của bạn (trong trường hợp này là google) và gửi bạn chuyển hướng sang google trang đăng nhập và một liên kết đến nơi bạn nên chuyển hướng sau (cho phép nói một)
  4. bạn có thể đăng nhập vào google cung cấp trang thông thường (sử dụng phương thức POST từ Python)
  5. Google cung cấp một mã thông báo mật mã (Không chắc chắn về bước này) để trả lại yêu cầu đăng nhập của bạn
  6. Bạn gửi yêu cầu mới st đến a bằng mã thông báo này.
  7. Stackoverflow sẽ liên hệ với google bằng mã thông báo này. Nếu tính xác thực được thiết lập, nó sẽ trả lại ID phiên
  8. Yêu cầu sau đó tới STackOverflow phải có ID phiên này
  9. Không có ý tưởng về đăng xuất !!

This link cho biết về các phản hồi khác nhau trong OpenID và ý nghĩa của chúng. Vì vậy, có lẽ nó sẽ có ích khi mã của bạn khách hàng của bạn.

Liên kết từ trang wiki OpenID Explained

EDIT: Sử dụng dữ liệu Tamper Thêm on cho Firefox, trình tự sau các sự kiện có thể được xây dựng.

  1. Người dùng gửi yêu cầu đến trang đăng nhập SO. Khi nhập openID trong trường biểu mẫu, trang kết quả sẽ gửi 302 chuyển hướng đến trang google. URL chuyển hướng có nhiều thông số OpenID (dành cho máy chủ google). Một trong số họ là return_to = https://stackoverflow.com/users/authenticate/?s=some_value.
  2. Người dùng được hiển thị với trang đăng nhập google. Khi đăng nhập, có một số 302 chuyển hướng người dùng xung quanh trong lĩnh vực google.
  3. Cuối cùng một 302 được nhận mà chuyển hướng người dùng của stackoverflow trang quy định tại 'return_to' trước
  4. Trong toàn bộ loạt bài này hoạt động rất nhiều của Cookie đã được tạo ra mà phải được lưu trữ một cách chính xác
  5. On truy cập vào SO trang (được 302'd bởi google), máy chủ SO xử lý yêu cầu của bạn và trong tiêu đề phản hồi gửi trường "Set-Cookie" để đặt cookie có tên gauth và usr với giá trị cùng với 302 khác để stackoverflow.com. Bước này hoàn tất thông tin đăng nhập của bạn
  6. Khách hàng của bạn chỉ cần lưu trữ cookie usr
  7. Bạn đã đăng nhập miễn là bạn gửi lại yêu cầu Cookie với bất kỳ yêu cầu nào đến SO.
  8. Bây giờ, bạn có thể yêu cầu hộp thư đến của mình chỉ cần gửi lại cookie để gửi yêu cầu.

Tôi khuyên bạn nên bắt đầu mã hóa ứng dụng khách trăn của mình và nghiên cứu phản hồi một cách cẩn thận. Trong hầu hết các trường hợp, nó sẽ là một loạt 302 với sự can thiệp của người dùng tối thiểu (ngoại trừ việc điền tên người dùng và mật khẩu Google của bạn và cho phép trang của trang web).

Tuy nhiên để dễ dàng hơn, bạn chỉ có thể đăng nhập vào SO bằng trình duyệt, sao chép tất cả các giá trị cookie và thực hiện yêu cầu bằng cách sử dụng urllib2 với các giá trị cookie được đặt.

Tất nhiên trong trường hợp bạn đăng xuất trên trình duyệt, bạn sẽ phải đăng nhập lại và thay đổi giá trị cookie trong chương trình python của bạn.

+0

OK, vì vậy tôi đăng nhập vào StackOverflow, chuyển hướng tôi đến trang đăng nhập của ví dụ. Google, một khi đã đăng nhập, chuyển hướng tôi trở lại SO một lần nữa. Nhưng bạn yêu cầu URL nào để đăng nhập vào SO? Thông thường, bạn yêu cầu một URL, gửi cho bạn một lỗi 301, và sau đó bạn nhận được các tiêu đề xác thực, sau đó bạn thêm lược đồ và lĩnh vực vào tiêu đề yêu cầu, cùng với tên người dùng và mật khẩu. Bây giờ, nếu tôi yêu cầu http://stackoverflow.com hoặc http://stackoverflow.com/users/login, tôi không nhận được bất kỳ Lỗi 301 nào ngay từ đầu. – Benjamin

+0

Tôi nghĩ rằng một khi bạn đăng nhập vào tài khoản google của bạn, nó sẽ gửi một chuyển hướng trở lại SO (Sau khi xác thực thành công và cho phép trang web đó là). URL chuyển hướng phải có thông số chứa mã thông báo mật mã do Google tạo cho SO. Khi bạn chuyển hướng đến trang SO đó, máy chủ SO xử lý mã thông báo và nếu được tìm thấy là đúng, hãy đặt cookie cho phần còn lại của phiên của bạn. Sau đó, bạn đã đăng nhập vào SO. Bạn có thể yêu cầu bất kỳ trang nào từ SO miễn là bạn gửi cookie theo yêu cầu và SO sẽ phục vụ trang đó cho bạn. – RedBaron

+0

Sử dụng firefox Tôi phát hiện ra rằng trang sau được truy cập sau khi tôi đăng nhập vào tài khoản google. http://stackauth.com/auth/global/write?authToken=SomeValue. Bây giờ liệu google chuyển hướng đến trang web này hay SO có trong giao tiếp trước đó đã nói với trình duyệt để chuyển hướng đến liên kết này khi auth hoàn tất mà tôi không biết. Bạn có thể phân tích cú pháp phản hồi của từng yêu cầu và phân tích nó để bạn có thể tìm thấy liên kết bị thiếu (Thiếu 301 phản hồi) – RedBaron

0

Tôi đã tạo một tập lệnh đơn giản để đăng nhập vào stackoverflow.com bằng cookie Mozilla Firefox. Nó không hoàn toàn tự động, bởi vì bạn cần phải đăng nhập bằng tay, nhưng đó là tất cả tôi quản lý để làm.

Scipt là thực tế cho các phiên bản mới nhất của FF (tôi đang sử dụng 8.0.1), nhưng bạn cần phải nhận được dll sqlite mới nhất, bởi vì mặc định một mà đi kèm với python 2,7 không thể mở DB. Bạn có thể lấy nó ở đây: 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' 
+1

Có thể bạn đã biết về các lỗi trong mã này, nhưng tôi đề cập đến chúng để mọi người đều biết. 1. Không di động (chỉ hoạt động trên Windows). 2. Không hoạt động nếu có nhiều hơn 1 hồ sơ firefox 3. Không hoạt động nếu người dùng không đăng nhập trong vòng 60 giây 4. Phải đợi ngay cả khi đăng nhập nhanh hơn 60 giây 5. Vô dụng phía máy chủ – rds

5

Tôi biết điều này là gần với khảo cổ học, đào một bài đó là hai tuổi, nhưng tôi chỉ viết một phiên bản nâng cao mới của mã từ câu trả lời xác nhận, vì vậy tôi nghĩ rằng nó có thể được mát mẻ để chia sẻ nó ở đây , như câu hỏi/câu trả lời này đã là một trợ giúp lớn cho tôi để thực hiện điều đó.

Vì vậy, đây là những gì là khác nhau:

  • nó sử dụng requests thư viện mới mà là một nâng cao hơn urllib2;
  • nó hỗ trợ xác thực bằng cách sử dụng nhà cung cấp openid của google và stackexchange.
  • đó là cách ngắn hơn và đơn giản hơn để đọc, dù nó có bản in ít

Dưới đây là các mã:

#!/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() 

mã là cũng có sẵn như là một github gist

HTH

+0

và tôi đã xuất bản một dự án bằng cách sử dụng để kết nối với stackoverflow (và trò chuyện) bằng cách sử dụng công cụ dòng lệnh: http://github.com/guyzmo/pystackoverflow – zmo

Các vấn đề liên quan