OAuth Step by Step

最近琢磨OAuth认证方式。OAuth的优点主要在于

  • 用户不需要直接提供用户名密码给第三方应用,就可以让第三方应用访问受限资源;
  • 资源提供方对第三方应用有更细粒度的控制。

在整个OAuth协议里,生成signature的base string是最容易出错的部分。它由HTTP方法名、URL编码的请求路径和请求的参数表组成。
请求的参数表是除去oauth_signature以外的所有参数,按参数名排序,并进行url转义

def to_signature_key(method, url, data):
	keys = list(data.keys())
	keys.sort()
	encoded = urllib.quote("&".join([key+"="+data[key] for key in keys]))
	return "&".join([method, urllib.quote(url, safe="~"), encoded])

有了这个通用的生成signature base string的方法,以后就可以根据OAuth协议规范按步骤进行。

首先获取Request Token。这一步通常使用资源提供方注册的API Key和API Key Secret

def request_token_params(consumer_key, consumer_secret, path, method='GET'):
	data={}
	data['oauth_consumer_key']=consumer_key
	data['oauth_signature_method']='HMAC-SHA1'
	data['oauth_timestamp']=str(int(time.time()))
	data['oauth_nonce']=''.join([str(random.randint(0,9)) for i in range(10)])
	print data

	msg = to_signature_key(method, path, data)
	print msg

	signed = base64.b64encode(hmac.new(consumer_secret+"&", msg, hashlib.sha1).digest())
	print signed
	data['oauth_signature']=signed
	return data

def result2dict(result_string):
	d = {}
	params = res.split('&')
	for p in params:
		d[p.split('=')[0]] = p.split('=')[1]
	return d

conn = httplib.HTTPConnection("www.douban.com", 80)

params = request_token_params(consumer_key, consumer_secret, request_token_path)
conn.request('GET', request_token_path+"?"+urllib.urlencode(params))
res = conn.getresponse().read()
print res
request_token = result2dict(res)

这一步可以获得未经认证的Request Token和Request Token Secret。需要注意的细节是在计算hmac签名的时候,即使只有一个Token Secret,仍然需要加上”&”

第二步要求用户授权该Request Token,打开浏览器,将用户定向到相应的授权页面,参数为上一步获得的Request Token

第三步,用授权过的Request Token换取Access Token。这一步类似第一步,只是用于签名的token包括API Key Secret和Request Token

def access_token_params(consumer_key, consumer_secret, oauth_token, oauth_secret, path, method='GET'):
	data={}
	data['oauth_consumer_key']=consumer_key
	data['oauth_signature_method']='HMAC-SHA1'
	data['oauth_timestamp']=str(int(time.time()))
	data['oauth_nonce']=''.join([str(random.randint(0,9)) for i in range(10)])
	data['oauth_token'] = oauth_token

	msg = to_signature_key(method, path, data)
	print msg

	signed = base64.b64encode(hmac.new(consumer_secret+"&"+oauth_secret, msg, hashlib.sha1).digest())
	print signed
	data['oauth_signature']=signed
	return data

params = access_token_params(consumer_key, consumer_secret, request_token['oauth_token'],
	request_token['oauth_token_secret'], access_token_path)
conn.request('GET', access_token_path+"?"+urllib.urlencode(params))
res = conn.getresponse().read()
print res
access_token = result2dict(res)

这一步将至少返回Access Token和Access Token Secret,是最终用于访问受限资源的Token。以豆瓣的实现为例,OAuth的相关参数应放在HTTP头里随请求进行发送。

def oauth_header(consumer_key, consumer_secret, oauth_token, oauth_secret, path, realm):
	data = access_token_params(consumer_key, consumer_secret, oauth_token, oauth_secret, path, method="POST")
	header_string = ','.join([key+'="'+data[key]+'"' for key in data.keys()])
	return 'OAuth realm="'+realm+'",'+header_string

posturl = 'http://api.douban.com/miniblog/saying'

content = """<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns:ns0="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/">
<content>li lei ju le han mei mei</content>
</entry>
"""

header = {}
header['Authorization'] = oauth_header(consumer_key, consumer_secret,
		access_token['oauth_token'], access_token['oauth_token_secret'],
		posturl, "http://api.douban.com")
header['Content-Type'] = 'application/atom+xml'
print header

conn.request('POST', posturl, content, header)
res = conn.getresponse().read()

print res

conn.close()

在这一步中,用于生成signature base string的url是要访问的受限资源地址,而签名的参数表依然是oauth相关的参数。
生成的Authorization头如下

Authorization: OAuth realm="http://api.douban.com",
    oauth_nonce="8735717688",
    oauth_timestamp="1262613619",
    oauth_consumer_key="0bc081a01168b263234184e0343a1729",
    oauth_signature_method="HMAC-SHA1",
    oauth_token="5fb836c37543ad691f28a44a5fcb083b",
    oauth_signature="jk6p5qaXVPrGQctSzpO5jjYHfDk="

用这个头就可以在一定的时间内访问所有授权范围内的受限资源。

代码是ugly了一些,不过应该相对易于理解吧。

This entry was written by Sunng , posted on Monday January 04 2010at 11:01 pm , filed under 手艺 and tagged , , , . Bookmark the permalink . Post a comment below or leave a trackback: Trackback URL.

One Response to “OAuth Step by Step”

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Click to hear an audio file of the anti-spam word