OAuth2.0概念
OAuth2.0是现在第三方授权主要采取的一种协议。
简易的说就是:第三方应用在不知道用户的用户名、密码等认证ID的情况下想要访问该用户的资源,可通过该用户授权的方式,获取相应权限,访问相应资源。
学习这个授权协议的时候,因为网上有太多的开放平台和介绍,OAuth又有1.0和2.0两个版本,因此看上去很混乱。
你有时候不知道自己看的是1.0的协议介绍,还是2.0的协议流程。因为二者的流程相似处是很大的,2.0是在1.0上做的一次简化,但2.0不会向下兼容1.0.
这里讲的是OAuth2.0的内容,不涉及1.0.
在网上,我看到最详尽和符合我思维习惯的一篇解释,网址如下:
深入理解OAuth2.0协议
这里有很详尽的介绍了OAuth2.0的概念、安全机制、协议流程等。这里简单的引用几幅流程图,作为解释,想要了解OAuth2.0的朋友,建议先看看链接中的一些概念。
OAuth2.0流程
说流程前,先介绍下OAuth2.0中涉及的所有参与者:
OAuth2.0的参与实体至少有如下三个:
· RO (resource owner): 资源所有者,对资源具有授权能力的人。
· RS (resource server): 资源服务器,它存储资源,并处理对资源的访问请求。
· Client: 第三方应用,它获得RO的授权后便可以去访问RO的资源。
此外,为了支持开放授权功能以及更好地描述开放授权协议,OAuth2.0引入了第四个参与实体:
· AS (authorization server): 授权服务器,它认证RO的身份,为RO提供授权审批流程,并最终颁发授权令牌(Access Token)。读者请注意,为了便于协议的描述,这里只是在逻辑上把AS与RS区分开来;在物理上,AS与RS的功能可以由同一个服务器来提供服务。
OAuth2.0的流程,先看下方图示:
这里解释下图中标志意义:
(1)1、2、3、4、5、6表示步骤;
(2)Authiorization Request:授权请求;Authorization Grant:授权授予;Access Token:访问令牌;Protected Resource:用户保护资源。
流程,我们描述如下:
(1)第三方应用(client)向资源拥有者(Resource owner)申请访问用户的某些资源——发起授权请求;
(2)资源拥有者如果同意,则授权该第三方应用可使用这些资源——授予授权;
(3)第三方应用使用资源拥有者给予的授权从授权服务端获取到访问令牌——Access Token;
(4)第三方应用使用Access Token获取到想要的资源信息——Protected Resource;
以上是OAuth2.0的主要流程,而OAuth2.0的授权方式也分多种:授权码 (Authorization Code Grant)、隐式授权 (Implicit Grant)、RO凭证授权 (Resource Owner Password Credentials Grant)、Client凭证授权 (Client Credentials Grant)
在日常的使用中,最常见的OAuth2.0协议的方式,是使用授权码的方式,我也只实验过授权码方式,因此后续也就以授权码方式来做测试。
授权码类型可图示如下,理解了上面的OAuth2.0流程,这个就毫无难度:
OAuth2.0协议的应用
明白了协议的意义和流程。那我们如何使用该协议,来达到我们要的目标呢。
这里以QQ登录的第三方授权作为实例,描述具体如何使用该协议。我们将协议与具体步骤一一对应。
首先,我们需要有一个第三方应用。这个应用,我们可以在腾讯的平台上创建。
(1)注册成为腾讯开放平台的开发者: 注册开发者
(2)创建一个移动应用app: 创建流程
注意:这里在创建一个应用时,我选择的是移动应用,不是网站。在移动应用中也没有看到设置回调地址的地方....因此,后续的实例,将是以引用腾讯提供的SDK的形式来描述的,而不是自定义的形式。
(3)腾讯中介绍如何实现OAuth2.0: 实现QQ登录
但在使用腾讯开放平台的SDK和提示步骤的过程中。发现有些地方不能匹配,SDK和文档不能很好的对应,导致按这个文档进行不下去。因此就修改了一些代码。
先贴上代码,如下:
package com.example.oauth_qq_csdn;
import org.json.JSONObject;
import com.tencent.connect.UserInfo;
import com.tencent.tauth.IUiListener;
import com.tencent.tauth.Tencent;
import com.tencent.tauth.UiError;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.app.Activity;
import android.content.Intent;
public class MainActivity extends Activity implements OnClickListener{
String TAG="tag";
Button btnButton,btnButton2,btnButton3;
TextView showTextView;
Tencent mTencent;
String APP_ID="1105014617";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//QQ登录按钮
btnButton=(Button)findViewById(R.id.qq);
btnButton.setOnClickListener(this);
//QQ登出按钮
btnButton3=(Button)findViewById(R.id.out);
btnButton3.setOnClickListener(this);
//用户信息获取按钮
btnButton2=(Button)findViewById(R.id.info);
btnButton2.setOnClickListener(this);
//用户信息显示
showTextView=(TextView)findViewById(R.id.show);
}
/**
* 登出
*/
public void logout()
{
mTencent.logout(this);
}
/**
* 登录QQ,scope取all,便于测试。
*/
public void login()
{
mTencent = Tencent.createInstance(APP_ID, getApplicationContext());
if (!mTencent.isSessionValid())
{
mTencent.login(this, "all", new LoginUiListener());
}
}
/**
* 获取用户信息
*/
public void getUserInfo()
{
UserInfo userinfo =new UserInfo(MainActivity.this, mTencent.getQQToken());
/**
* 异步获取信息,用回调监听
*/
userinfo.getUserInfo(new UserInfoListener());
}
/**
* 必要加上,用于登录的监听回调
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
//mTencent.onActivityResultData(requestCode,resultCode,data);
Tencent.onActivityResultData(requestCode, resultCode, data, new LoginUiListener());
}
/**
* 获取用户信息时的监听
* @author 战国
*
*/
private class UserInfoListener implements IUiListener
{
@Override
public void onCancel() {
// TODO Auto-generated method stub
}
@Override
public void onComplete(Object arg0) {
// TODO Auto-generated method stub
try {
showTextView.setText(String.valueOf(arg0));
} catch (Exception e) {
// TODO: handle exception
}
}
@Override
public void onError(UiError arg0) {
// TODO Auto-generated method stub
}
}
/**
* 登录时候的监听
* @author 战国
*
*/
private class LoginUiListener implements IUiListener {
@Override
public void onCancel() {
// TODO Auto-generated method stub
}
@Override
public void onComplete(Object value) {
// TODO Auto-generated method stub
Log.i(TAG, value.toString());
System.out.println("有数据返回..");
try {
JSONObject jo = (JSONObject) value;
int ret = jo.getInt("ret");
System.out.println("json=" + String.valueOf(jo));
if (ret == 0) {
Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_LONG).show();
//登录成功返回的必要信息
String openID = jo.getString("openid");
String accessToken = jo.getString("access_token");
String expires = jo.getString("expires_in");
//登录成功后,必要设置openid、访问令牌accessToken信息,用于获取用户信息
mTencent.setOpenId(openID);
mTencent.setAccessToken(accessToken, expires);
}
} catch (Exception e) {
// TODO: handle exception
}
}
@Override
public void onError(UiError arg0) {
// TODO Auto-generated method stub
}
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.qq:
login();
break;
case R.id.info:
getUserInfo();
break;
case R.id.out:
logout();
break;
default:
break;
}
}
}
代码中,主要实现了三个功能:QQ第三方授权登录、获取基本信息、QQ登出。
因为不知道在本地应用中如何设置回调地址,我就只能以腾讯提供的SDK的形式,来实现第三方的授权登录。
上面的代码中,有几个地方需要注意:
(1)onActivityResult中的监听传入;
(2)QQ授权成功后,要传入openid和access token作为参数;
看代码中的步骤,应该知道,OAuth的有些步骤,因为SDK的封装,我们看不到了。那具体是怎么对应的呢?
(1)腾讯SDK的login直接返回了access token、openid等信息。这就对应着OAuth2.0中的第三方应用向用户申请授权,用户返回授权码+第三方应用用授权码获取access token,这两个步骤;
(2)腾讯SDK的获取用户信息,就对应着OAuth2.0的第三方应用使用本身id(openid)和access token,获取用户资源的过程。
至于,其他开放平台的授权,采用的都是大同小异的方式。