如何请求使用OpenID的网站页面?
这个问题之前有人问过。被接受的答案对提问者和回答者来说可能很明显,但对我来说却不然。我在上面的问题下评论想要更详细的信息,但没有得到回复。我也去问了元社区,想知道如何让问题重新被关注,但也没有得到答案。
上面问题的答案是:
从客户端的角度来看,OpenID登录和其他网页登录非常相似。客户端没有一个明确的协议;这只是一个普通的网页会话,具体情况取决于你的OpenID提供者。因此,我怀疑是否有这样的库存在。你可能需要自己编写代码。
我已经知道如何用Python 登录一个网站,使用的是Urllib2模块。但这还不足以让我猜出如何进行OpenID认证。
我实际上是想获取我的StackOverflow收件箱的json格式,为此我需要先登录。
有没有人能提供一个简单的介绍或者链接到一个不错的教程,教我怎么做?
5 个回答
我知道这就像考古学一样,挖掘一个两年前的帖子,但我刚写了一个新版本的代码,改进了之前被验证的答案,所以我觉得在这里分享一下可能会很不错,因为这个问题和答案对我实现这个功能帮助很大。
那么,这里有什么不同呢:
- 它使用了新的
requests
库,这比urllib2
更好; - 它支持使用谷歌和 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 上找到。
希望对你有帮助!
我自己对OpenID了解不多,但你的帖子(还有悬赏!!)让我产生了兴趣。
这个链接详细介绍了OpenID认证的流程(至少是1.0版本,新版本是2.0)。从我理解的内容来看,步骤大概是这样的:
- 你打开stackoverflow的登录页面,那里会有一个使用OpenID登录的选项(作为一个表单字段)。
- 你发送你的OpenID,实际上它是一种URI,而不是用户名或邮箱(如果是Google账号,那就是你的个人资料ID)。
- 然后stackoverflow会连接到你的ID提供商(在这个例子中是Google),并把你重定向到Google的登录页面,同时给你一个链接,稍后你需要重定向到这个链接(假设是a)。
- 你可以在Google提供的页面上正常登录(使用Python的POST方法)。
- Google会在你登录请求后返回一个加密的令牌(这一步我不是很确定)。
- 你用这个令牌向a发送新的请求。
- stackoverflow会用这个令牌联系Google。如果验证通过,它会返回一个会话ID。
- 之后对StackOverflow的请求都需要带上这个会话ID。
- 关于登出我就不太清楚了!!
这个链接介绍了OpenID中的各种响应及其含义。所以当你编写客户端代码时,可能会很有用。
来自维基页面的链接 OpenID Explained
编辑:使用Firefox的Tamper Data插件,可以构建以下事件序列。
- 用户向SO登录页面发送请求。在表单字段中输入OpenID后,结果页面会发送一个302重定向到Google页面。重定向的URL包含很多OpenID参数(这些是给Google服务器的)。其中一个参数是return_to=https://stackoverflow.com/users/authenticate/?s=some_value。
- 用户看到Google的登录页面。登录后会有几个302重定向,用户在Google的环境中被重定向。
- 最后收到一个302重定向,用户被引导到之前在'return_to'中指定的stackoverflow页面。
- 在整个操作过程中,会生成很多cookie,必须正确存储。
- 访问SO页面(之前被Google重定向的),SO服务器处理你的请求,并在响应头中发送一个“Set-Cookie”字段,设置名为gauth和usr的cookie,并再重定向到stackoverflow.com。这一步完成了你的登录。
- 你的客户端只需存储cookie usr。
- 只要记得在任何请求中发送Cookie usr,你就已经登录了。
- 现在你可以请求你的收件箱,只需记得在请求中发送usr cookie。
我建议你开始编写Python客户端,并仔细研究响应。在大多数情况下,这将是一系列302重定向,用户干预很少(除了填写Google的用户名和密码以及允许网站页面)。
不过为了简化,你可以直接在浏览器中登录SO,复制所有cookie值,然后使用urllib2发送请求,设置这些cookie值。
当然,如果你在浏览器中登出,你需要再次登录并在Python程序中更改cookie值。
这个回答总结了下面其他人说的内容,特别是RedBaron的观点,并且添加了一种我用来通过谷歌账户访问StackOverflow收件箱的方法。
使用Firefox的Tamper Data开发者工具并登录StackOverflow,可以看到OpenID的工作原理如下:
- StackOverflow向一个指定的服务(这里是谷歌)请求认证,这个服务在发送的数据中定义;
- 谷歌账户接管这个过程,检查是否已经存在一个cookie来证明认证;
- 如果没有找到cookie,谷歌会请求认证并设置一个cookie;
- 一旦cookie被设置,StackOverflow就会确认用户的认证。
以上总结了这个过程,实际上要复杂得多,因为确实会有很多重定向和cookie的交换。
因为用程序重复这个过程有点困难(可能是我不太懂),特别是要找到所有带有地区特定信息的URL。我选择先登录谷歌账户,获取一个合适的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)