原文:http://www.littlear.com/post/2011/01/0285/
由于最近在开发一个关于微博整合的小应用,于是开始接触各大微博平台的开放平台(新浪、搜狐、网易、QQ):目前这几大微博的应用开发都采用OAuth授权,要访问大部分API都需要OAuth方式的身份鉴权。
OAuth是什么?
先来简单介绍一下OAuth授权协议:OAUTH协议为用户资源的授权提供了一个安全的、开放而又简易的标准。同时,任何第三方都可以使用OAuth认证服务,任何服务提供商都可以实现自身的OAuth认证服务,因而OAUTH是开放的。业界提供了OAuth的多种实现如PHP,JavaScript,Java,Ruby等各种语言开发包,大大节约了程序员的时间,因而OAuth是简易的。目前互联网很多服务如Open API,很多大头公司如Google,Yahoo,Microsoft等都提供了OAuth认证服务,这些都足以说明OAUTH标准逐渐成为开放资源授权的标准。
OAuth的授权流程
你所开发的应用需要流程如下:
- 向应用服务商(新浪、搜狐等微博)请求request_token。
- 得到request_token后重定向用户到服务商的授权页面。
- 如果用户选择授权你得应用,用request_token向服务商请求换取access_token。
- 得到access_token等信息访问受限资源。
而服务商相应的响应如下:
- 创建request_token返回给应用。
- 询问用户是否授权此应用。如果用户授权重定向用户至应用页面。
- 创建access_token并返回给应用。
- 响应受限资源请求并返回相关信息。
通俗点的说法就是“你拿着你得身份证明(request_token)向服务商申请进入用户家的门钥匙(access_token),服务商询问用户同不同意,如果用户同意服务商就给你进入用户家门的钥匙(access_token),拿到钥匙后你就可以进到用户家里”。
OAuth授权的Java实现
作为一个开放协议目前有很多现成的Oauth库可供开发者使用,可以点击这里下载。不过有精力有时间的话还是自己去实现一下OAuth授权的流程,可以很好的体会OAuth认证协议的原理。以
下面就是我使用Java实现Oauth的具体步骤,代码很简单,如果有画蛇添足的地方还望高手一笑而过。
一、获取Request_token
首先得准备一下参数及其来源:
- oauth_consumer_key —— 注册应用后由应用服务商提供
- consumer_secret —— 注册应用后由应用服务商提供
- oauth_callback —— 用户授权后的返回地址
- oauth_nonce —— 随机字符串,须保证每次都不同
- oauth_timestamp —— 时间戳
- oauth_signature_method —— 签名base string 的方法,目前支持 HMAC-SHA1
- oauth_version —— Oauth协议版本
还需要下面三个请求地址(这些地址任何一个提供OAuth的服务商都会提供给你,看下API文档就会找到):
- requst_token_url —— 上面第1步中的请求地址
- authorize_url —— 上面第2步的请求地址
- access_token_url —— 上面第3步的请求地址
至于如何注册应用,新浪微博点此,网易微博点此,腾讯微博点此,搜狐微博需要你发邮件索取,具体看这里。注册成功后就会获得oauth_consumer_key 和 consumer_secret 两个参数。
oauth_callback 起的作用是当用户授权成功后服务商会把用户重定向到这个网址。
oauth_nonce 是一个随机字符串下面是我的生成代码:
public String set_nonce() {
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 18; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
oauth_timestamp 是请求的时间戳,我的代码如下:
public String set_timestamp() {
Date date = new Date();
long time = date.getTime();
return (time + "").substring(0, 10);
}
需要说明一下的是这里的时间戳为10位而不是13位,因此截取0-10位置。
其他参数直接指定就行了。
接下来,有了这些参数就可以组装base string了。准备base string的目的就是为了得到 oauth_signature 这个参数,这个参数向服务商发送请求的时候需要用到。
组装的方法是用下面8部分
- POST(也可以是GET,取决于你应用服务商支持哪个)。
- Urlencode之后的requst_token_url 。
- oauth_callback=Urlencode之后你的oauth_callback(Urlencode的参数为“utf-8”)。
- oauth_consumer_key = 你的oauth_consumer_key
- oauth_nonce = 你的oauth_nonce
- oauth_signature_method = 你的 oauth_signature_method
- oauth_timestamp = 你的oauth_timestamp
- oauth_version = “1.0”——目前大多数OAuth都采用的是1.0或1.0a版本。
下面是我的Java实现代码:
public String set_basestring() throws UnsupportedEncodingException {
String bss;
bss = oauth_request_method + "&"
+ URLEncoder.encode(requst_token_url, "utf-8") + "&";
String bsss = "oauth_callback="
+ URLEncoder.encode(oauth_callback, "utf-8")
+ "&oauth_consumer_key=" + oauth_consumer_key + "&oauth_nonce="
+ oauth_nonce + "&oauth_signature_method="
+ oauth_signature_method + "&oauth_timestamp="
+ oauth_timestamp + "&oauth_version=" + oauth_version;
bsss = URLEncoder.encode(bsss, "utf-8");
return bss + bsss;
}
有了base string就可以签名生成oauth_signature这个参数,oauth_signature会在请求request_token的时候用到。签名算法是HMAC-SHA1,签名的key就是最开始的consumer_secret后加一个&号,签名算法代码如下:
public String hmacsha1(String data, String key) {
byte[] byteHMAC = null;
try {
Mac mac = Mac.getInstance("HmacSHA1");
SecretKeySpec spec = new SecretKeySpec(key.getBytes(), "HmacSHA1");
mac.init(spec);
byteHMAC = mac.doFinal(data.getBytes());
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException ignore) {
}
String oauth = new BASE64Encoder().encode(byteHMAC);
return oauth;
}
里面用的的BASE64Encoder这个类可以Google一个。
得到oauth_signature后就要开始向 requst_token_url 发送请求了,OAuth规范定义了三种传递OAuth参数方式:
- httpheader中
- url中
- post form中
国内各大微博的支持情况是:新浪Httpheader可用,网易Httpheader可用,腾讯只支持在url,搜狐由于没有appkey所以还没去尝试。
如果使用Httpheader传递参数头名为“Authorization”,值为下面的格式,将值改为自己应用的。
OAuth oauth_nonce="9zWH6qe0qG7Lc1telCn7FhUbLyVdjEaL3MO5uHxn8", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1272323047", oauth_consumer_key="GDdmIQH6jhtmLUypg82g", oauth_token="8ldIZyxQeVrFZXFOZH5tAwj6vzJYuLQpl0WUEYtWc", oauth_verifier="pDNg57prOHapMbhv25RNf75lVRd6JDsni1AJJIDYoTY", oauth_signature="PUw%2FdHA4fnlJYM6RhXk5IU%2F0fCc%3D", oauth_version="1.0"
url和post form两种方式的参数名和参数值也即上面的,完全一样。
请求发送成功后就会得到的响应如下:
oauth_token=8ldIZyxQeVrFZXFOZH5tAwj6vzJYuLQpl0WUEYtWc&oauth_token_secret=x6qpRnlEmW9JbQn4PQVVeVG8ZLPEx6A0TOebgwcuA&oauth_callback_confirmed=true
可以看到响应里面已经包含oauth_token和oauth_token_secret了,存贮之以备后面使用。
二、用户认证
拿到了oauth_token之后就需要用户对此oauth_token授权,也即对你的应用授权,具体做法就是发送oauth_token到服务商并请求用户对此oauth_token授权:实现方法为以oauth_token和oauth_callback为参数请求oauthorize_url,Servlet中的代码如下:
resp.sendRedirect(oauthorize_url+"?oauth_token="+oauth_token+"&oauth_callback="+oauth_callback);
这是用户就被带到了应用授权页面,并可以选择是否对该应用授权。如果用户授权之后就会被带到oauth_callback 地址。同时如果需要服务商会给oauth_callback返会一个名为oauth_verifier的参数(此参数用于无法跳转的桌面应用,不一定每个微博平台都会返回),这时候我们的oauth_token已经获得用户的授权了。
三、用oauth_token换取access_token
这一步跟第一步“获取Request_token”基本相同,也是需要准备 base string 对其签名,然后发送请求,可以参考第一步的代码实现:但是相应的参数有所不用,具体来讲就是第一步组装base string 时候8个部分中第二个部分中的url换为access_token_url 并去掉oauth_callback加上oauth_token(如果有oauth_verifier的话也需要一并加上),组装好之后需要签名以得到oauth_signature,本次签名的方法跟上次一样,但是key变为consumer_secret和oauth_token_secret以&连接的串。
下来需要向access_token_url发送请求,请求参数包括base string 里的除了请求方法(POST或GET)和请求地址外的所有参数及其值和签名后生成的oauth_signature。例子如下:
OAuth oauth_nonce="9zWH6qe0qG7Lc1telCn7FhUbLyVdjEaL3MO5uHxn8", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1272323047", oauth_consumer_key="GDdmIQH6jhtmLUypg82g", oauth_token="8ldIZyxQeVrFZXFOZH5tAwj6vzJYuLQpl0WUEYtWc", oauth_verifier="pDNg57prOHapMbhv25RNf75lVRd6JDsni1AJJIDYoTY", oauth_signature="PUw%2FdHA4fnlJYM6RhXk5IU%2F0fCc%3D", oauth_version="1.0""
请求成功会服务商就会返回oauth_token和oaut_token_secret,这里的oauth_token和oaut_token_secret就是真正访问资源要用的access_token。
还需要说明的是以上过程只需要经行一次,就是说你拿到的access_token是不会过期的,除非用户手动将授权收回,因此作为access_token的oauth_token和oaut_token_secret要保存起来,以后访问受限资源的时候可以直接使用。至于如何访问受限资源,等以后有时间了再补上。
(本文系原创,转载请注明出处)