本文分两部分,第一部分介绍OAuth基础知识,第二部分展示一个Demo,使用百度OAuth SDK获取令牌信息与用户信息。
详细代码:github.com/Baolvlv/LearnAndroid/tree/master/OAuthDemo
一、OAuth基础知识
1.OAuth概述与运行流程
OAuth是关于授权(authorization)的开放的网络标准,用户允许第三方应用访问用户在某一网站上的私密资源,而不需将用户名与密码提供给第三方应用,通过OAuth访问可以限制第三方应用的范围和有效期。
2006年11月,OpenID不能满足twitter的委托授权
2007年4月,成立OAuth讨论小组
2007年11月,OAuth核心1.0最后的草案发布
2010年4月,OAuth1.0协议发布 RFC-5849
2012年12月,OAuth2.0协议发布 RFC-6749
OAuth 2.0不向下兼容OAuth 1.0
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
|第三方应用 |<-(D)----- Access Token ———| 授权服务器 |
| | (令牌) +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +———————+
2.OAuth授权模式
1.授权码模式(Authorization Code)
2.简化模式(implicit grant)
3.密码模式(resource owner password credentials)
4.客户端模式(client credentials)
授权码模式最完整严密,下图为授权码模式流程
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI ---->| |
| User- | | Authorization |
| Agent -+----(B)-- User authenticates --->| Server |
| | | |
用户代理 -+----(C)-- Authorization Code ---<| |
+-|----|---+ +---------------+
| | ^ v
(A) (C) | |
| | | |
^ v | |
+---------+ | |
| |>---(D)-- Authorization Code ---------' |
| Client | & Redirection URI |
| | |
| |<---(E)----- Access Token -------------------'
+---------+ (w/ Optional Refresh Token)
Note: The lines illustrating steps (A), (B), and (C) are broken into
two parts as they pass through the user-agent.
Figure 3: Authorization Code Flow
Redirection URI———重定向URIOptional Refresh Token———刷新令牌
客户端发起两次请求,第一次以获得临时授权码作为结束,第二次以获得令牌作为结束
用户代理一般为浏览器
第一次请求参数:
第一次请求返回参数:
第二次请求参数:
请求方式:
第二次请求需要客户端的ID与密钥
现在普遍使用Https,当使用Https时,get和post请求安全性相同,get方式更加便捷。
使用http+Basic时,使用post方式更为安全,请求信息不可以在url中直接读取,post会将请求内容隐藏到请求实体中。
Content-Type:申请的表单类型
grant_type:请求实体
Http Basic Authentication:
访问一个特定的域名或url时,需要通过用户名和密码进行授权查看
发起请求时携带用户名和密码信息:
post方式和get方式:
代码实现:post方式:
第二次请求返回参数:
access_token:令牌,expires_in:令牌的有效时间段
refresh_token:刷新令牌,令牌实效后获取新的授权码
3.OAuth授权模式:简化模式,密码模式,客户端模式
简化模式流程:
没有后台服务器,或不能妥善保管第三方密钥情况下:
web-Hosted Client Resource:内嵌到浏览器中的客户端模块
User-Agent:用户代理,一般为浏览器
开始请求部分与授权码模式相同,返回值为重定向url和令牌,认证服务器将令牌放置在url的哈希值部分(#后面的部分)一般认为是未知信息,转发到服务端哈希值会被忽略掉。
web资源模块处理重定向url,返回给用户代理一个脚本用于解析,用户代理完成解析后,返回给客户端令牌信息。
通常会将web-Hosted Client Resource部分内嵌到Client,直接截取收到的信息,提取令牌信息简化流程
简化模式请求参数:
简化模式请求与返回值:
没有刷新令牌的原因:不能妥善保管第三方密钥,有刷新令牌降低安全性
密码模式请求流程:
用户需要将用户名与密码告诉客户端,但客户端不得存储密码
用户对客户端足够的信任,认证服务器没有其他的认证方式可以选择时采用。
密码模式请求参数:
密码模式请求具体内容:
密码模式请求返回值:
密码模式请求返回值与授权码模式返回值类似
客户端模式请求流程:
用户在客户端注册,客户端提出请求,授权不明显
客户端模式请求参数:
客户端模式请求具体内容:
客户端模式请求返回值:
客户端模式的返回值与其他几种认证类型类似:
4.OAuth更新令牌与相关问题:
更新令牌的方法:
1.重复一遍认证流程,用户手动输入用户名密码,发起请求认证
2.使用refreshtoken获取一个新的令牌,即刷新令牌操作
更新令牌参数:
更新令牌具体请求过程:
4.1授权码模式中请求参数state的作用:
当多个用户发起请求时,如果没有state参数,认证服务器只会返回临时授权码这一个参数,容易混淆。添加state参数可以将发起请求的的用户与获得的临时授权码一一对应。对应方式不可预期以提高安全性。
4.2为什么需要临时授权这一过程:
(1)redirect_url的安全性
redirect_url由第三方提供,是一个可访问的server表单地址。如果被截获,令牌会被截获
(2)redirect_url不如access token敏感,被截获不会泄漏用户的资源信息。
(3)如果没有临时授权码这一步,需要增加整个认证过程中的安全性
4.3 申请百度开发者账号以及百度OAuth
http://developer.baidu.com/wiki/index.php?title=docs/oauth
4.4获取百度令牌
http://developer.baidu.com/wiki/index.php?title=docs/oauth/authorization
4.5获取百度用户信息
二、android工程中获取百度令牌信息
http://developer.baidu.com/wiki/index.php?title=docs/oauth/showcase
下载安卓相关的sdk,解压后将其中的 jar包导入工程。
Project Structure->添加app的dependencies,添加百度相关的jar包
创建Baidu对象,调用authorize方法,第四个参数新建百度对话框监听器对象,实现其完成,异常,错误,取消四个方法.
//第一个参数为clientId,即API Key
finalBaidu baidu =newBaidu("PRheS6ajilCjg1muyFDGeCYz",this);
baidu.authorize(this, true, true, newBaiduDialog.BaiduDialogListener() {
@Override
public voidonComplete(Bundle bundle) {
//获取baidu对象中的AccessToken
refreshUI(baidu.getAccessToken());
}
@Override
public voidonBaiduException(BaiduException e) {
refreshUI("exception");
}
@Override
public voidonError(BaiduDialogError baiduDialogError) {
refreshUI("error");
}
@Override
public voidonCancel() {
refreshUI("cancel");
}
});
在主线程中,创建私有方法,用以刷新ui
private voidrefreshUI(finalString msg){
runOnUiThread(newRunnable() {
@Override
public voidrun() {
tvAccessToken.setText(msg);
}
内部类中只能调用外部类的final对象
三、android工程中获取百度个人信息
由于网络请求不能放在主线程中,所以通过以下两种方式实现:
1.在主线程中新建线程,使用百度提供的api,调用Baidu的对象的request方法返回json文本
启动线程
newThread(){
@Override
public voidrun() {
String url="https://openapi.baidu.com/rest/2.0/passport/users/getInfo";
try{
//使用baidu对象request时,第二个参数会自动将包含在对象中的access Token传入
finalString jsonText =mBaidu.request(url,null,"GET");
//返回对象为简单类(实体时)时,使用class型
finalUserEntity user =mGson.fromJson(jsonText,UserEntity.class);
//返回对象为泛型时,使用Type方式
finalUserEntity user =mGson.fromJson(jsonText,newTypeToken(){}.getType());
//在主线程中实现runnable接口,完成ui的更新
runOnUiThread(newRunnable() {
@Override
public voidrun() {
tvResult.setText(jsonText);
tvUser.setText(mGson.toJson(user));
}
});
}catch(IOException e) {
e.printStackTrace();
}catch(BaiduException e) {
e.printStackTrace();
}
super.run();
}
}.start();
其中通过使用Gson库,完成json文本到json实体的转换或json实体到json文本的转换
首先实体化Gson对象
mGson=newGson();