`

OAuth认证介绍及腾讯微博OAuth认证示例

 
阅读更多

本文主要介绍OAuth的用处OAuth的流程腾讯微博OAuth认证示例(新浪、人人类似)以及一些认证的异常。

 

1、OAuth介绍

目前很多主流的用户权限认证都是用OAuth,像google、microsoft、yahoo、人人、新浪微博、腾讯微博。只不过各自使用的OAuth版本可能略有不同。

使用OAuth的一个好处就是在用户向服务器数据请求时,避免了每次都需要传输用户名和密码,通过access token和secret使得用户在正常访问数据的同时保证了用户帐号的安全性。

OAuth比较适合的web应用程序和提供服务器端api或者两者混合的场景,OAuth支持目前大部分的主流语言

更多关于OAuth见:http://www.oauth.net

 

2、OAuth流程

OAuth的流程最终的结果是为了得到可以访问数据的access token和ccess secret(可能没有),以后就通过此access token和access secret和服务器进行交互。

大致的流程分为三步(OAuth1.0和2.0可能有点差异):

a 先获得一个未授权的request token,或者叫request code

b 以上步的未授权的token换取授权的request token和request secret(可能没有),这一步之后一般会提示输入用户名、密码

c 使用上步授权后的request token换取access token和access secret(可能没有)

现在就得到了access token和ccess secret(可能没有),使用它们就可以同服务器交互访问数据,而不用每次传递用户名和密码

 

3、腾讯微博OAuth api介绍

目前腾讯微博使用的是OAuth1.0、新浪微博使用的是OAuth2.0、人人网使用的是OAuth2.0,这里只介绍腾讯微博,关于人人和新浪类似,大家可以自己修改。

因自己写的腾讯微博sdk中默认不带oauth认证过程,不少朋友问到如何进行认证,这里就大致贴代码介绍下,有点长,可看下大概明白意思,自己再根据需要精简。主要分为三个部分:

第一部分:调用认证函数,跳转到认证页面

认证函数如下

private static QqTSdkService qqTSdkService = new QqTSdkServiceImpl();

/**
 * OAuth部分参见http://wiki.open.t.qq.com/index.php/API%E6%96%87%E6%A1%A3#category_1
 */
@Override
public Intent auth(Context context, String callBackUrl) {
	Intent intent = new Intent();
	Bundle bundle = new Bundle();
	QqTAppAndToken qqTAppAndToken = new QqTAppAndToken();
	qqTAppAndToken.setAppKey(APP_KEY);
	qqTAppAndToken.setAppSecret(APP_SECRET);
	qqTSdkService.setQqTAppAndToken(qqTAppAndToken);
	Map<String, String> requestTokenMap = qqTSdkService.getUnAuthorizedRequestToken(callBackUrl);
	if (!MapUtils.isEmpty(requestTokenMap) && requestTokenMap.containsKey(QqTConstant.PARA_OAUTH_TOKEN)) {
		Map<String, String> parasMap = new HashMap<String, String>();
		parasMap.put(QqTConstant.PARA_OAUTH_TOKEN, requestTokenMap.get(QqTConstant.PARA_OAUTH_TOKEN));
		bundle.putString(SnsConstant.OAUTH_URL,
						 HttpUtils.getUrlWithParas(QqTConstant.GET_AUTHORIZATION_URL, parasMap));
		bundle.putString(SnsConstant.CALL_BACK_URL, callBackUrl);
		bundle.putString(SnsConstant.REQUEST_TOKEN_SECRET, requestTokenMap.get(SnsConstant.REQUEST_TOKEN_SECRET));
		intent.putExtras(bundle);
		intent.setClass(context, OAuthWebViewActivity.class);
	}
	return intent;
}

a. 两个参数第一个为activity中getApplicationContext();得到的context

第二个为认证成功返回的url,对于android的activity格式为"appName://activityClassName",其中appname

为应用名,activityClassName为activity的类名。为了认证后能正确跳转到activity,需要在AndroidManifest.xml中添加相应的activity的intent-filter如下,相当于host配置

<intent-filter>
    <data android:scheme="appName" android:host="activityClassName" />
</intent-filter>

b. QqTSdkService、MapUtils、QqTConstant、HttpUtils的引用见腾讯微博java(android) api 

c. SnsContant 中的一些常量定义如下

    /** 程序中用到的一些字符串常量 **/
    public static final String               WEBSITE_TYPE                          = "websiteType";
    public static final String               OAUTH_URL                             = "oAuthUrl";
    public static final String               CALL_BACK_URL                         = "callBackUrl";
    public static final String               REQUEST_TOKEN_SECRET                  = "oauth_token_secret";
    public static final String               STATUS_ID                             = "statusId";
    public static final String               COMMENT_TYPE                          = "commentType";
    public static final String               COMMENT_ID                            = "commentId";

d. OAuthWebViewActivity的就是认证页面,代码见第二部分

 

activity中调用认证函数

Intent intent = auth(context, "appName://activityClassName");
if (intent == null || intent.getExtras() == null
	|| !intent.getExtras().containsKey(SnsConstant.CALL_BACK_URL)) {
	// Toast.makeText(this, "进入认证页面失败", Toast.LENGTH_SHORT).show();
	return;
} else {
	startActivity(intent);
}

 

第二部分:进入认证页面 

OAuthWebViewActivity的代码如下,就是一个webview加载授权页面

package com.trinea.sns.activity;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.net.http.SslError;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.Window;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.trinea.sns.util.CodeRules;
import com.trinea.sns.util.SnsConstant;

/**
 * 认证的webView
 * 
 * @author Trinea 2012-3-20 下午08:42:41
 */
public class OAuthWebViewActivity extends Activity {

    private WebView                    authWebView = null;
    private Intent                     intent      = null;

    private String                     callBackUrl;
    private String                     requestTokenSecret;

    public static OAuthWebViewActivity webInstance = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_PROGRESS);
        setContentView(R.layout.web_view);
        setTitle("腾讯微博授权认证");

        webInstance = this;
        authWebView = (WebView)findViewById(R.id.authWebView);
        WebSettings webSettings = authWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setSaveFormData(true);
        webSettings.setSavePassword(true);
        webSettings.setSupportZoom(true);
        webSettings.setBuiltInZoomControls(true);
        webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);

        authWebView.setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                authWebView.requestFocus();
                return false;
            }
        });

        // 根据传递过来的信息,打开相应的授权页面
        intent = this.getIntent();
        if (!intent.equals(null)) {
            Bundle bundle = intent.getExtras();
            if (bundle != null && bundle.containsKey(SnsConstant.OAUTH_URL)) {
                authWebView.loadUrl(bundle.getString(SnsConstant.OAUTH_URL));
                if (bundle.getString(SnsConstant.CALL_BACK_URL) != null) {
                    callBackUrl = bundle.getString(SnsConstant.CALL_BACK_URL);
                }
                if (bundle.getString(SnsConstant.REQUEST_TOKEN_SECRET) != null) {
                    requestTokenSecret = bundle.getString(SnsConstant.REQUEST_TOKEN_SECRET);
                }
                authWebView.setWebChromeClient(new WebChromeClient() {

                    public void onProgressChanged(WebView view, int progress) {
                        setTitle("腾讯微博授权页面加载中,请稍候..." + progress + "%");
                        setProgress(progress * 100);

                        if (progress == 100) {
                            setTitle(R.string.app_name);
                        }
                    }
                });

                authWebView.setWebViewClient(new WebViewClient() {

                    @Override
                    public boolean shouldOverrideUrlLoading(WebView view, String url) {
                        view.loadUrl(url);
                        return true;
                    }

                    @Override
                    public void onPageStarted(WebView webView, String url, Bitmap favicon) {
                        if (url != null && url.startsWith(callBackUrl)) {
                            Class backClass = CodeRules.getActivityClass(CodeRules.getActivityNameFromUrl(callBackUrl));
                            if (backClass != null) {
                                Intent intent = new Intent(OAuthWebViewActivity.this, backClass);
                                Bundle backBundle = new Bundle();
                                backBundle.putString(SnsConstant.REQUEST_TOKEN_SECRET, requestTokenSecret);
                                intent.putExtras(backBundle);
                                Uri uri = Uri.parse(url);
                                intent.setData(uri);
                                startActivity(intent);
                            }
                        }
                    }
                });
            }
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
    protected void onStop() {
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    /**
     * 监听BACK键
     * 
     * @param keyCode
     * @param event
     * @return
     */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
            if (authWebView.canGoBack()) {
                authWebView.goBack();
            } else {
                // OAuthActivity.webInstance.finish();
                finish();
            }
            return true;
        }

        return super.onKeyDown(keyCode, event);
    }
}

a. R.layout.web_view为

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
	xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
	  <WebView	
	  	android:layout_height="wrap_content"
	    android:layout_width="wrap_content"
	  	android:id="@+id/authWebView">
	  </WebView>
</LinearLayout>

b. CodeRules.getActivityClass(CodeRules.getActivityNameFromUrl(callBackUrl));作用是从url中获得到activity对应的类,方便在跳转回activity,即根据"appName://activityClassName"得到activityClassName的Class

c. public void onPageStarted(WebView webView, String url, Bitmap favicon)表示监听webView页面开始加载事件

if (url != null && url.startsWith(callBackUrl)) 表示认证已经成功,开始加载callBackUrl("appName://activityClassName"),这个时候我们让它跳转到对应的activity,这个时候的url中已经包含了accessToken和accessSecret

 

在第一部分startActivity后跳转到认证页面,填入帐号和密码并点击授权便可进入上面c的onPageStarted,这个时候我们已经得到了accessToken和accessSecret

 

第三部分 认证返回处理

在返回的activity中添加OnNewIntent函数,需要在AndroidManifest.xml中添加相应的activity的属性android:launchMode="singleTask"

@Override
protected void onNewIntent(Intent intent) {
	super.onNewIntent(intent);

	Bundle bundle = intent.getExtras();
	if (bundle != null) {
		UserInfo userInfo = authBack(intent.getData(), bundle.getString(SnsConstant.REQUEST_TOKEN_SECRET));
		if (userInfo != null) {
			Toast.makeText(this, "获取用户信息失败,请重新验证", Toast.LENGTH_SHORT).show();
			OAuthWebViewActivity.webInstance.finish();
		} else {
			Toast.makeText(this, "获取用户信息失败,请重新验证", Toast.LENGTH_SHORT).show();
		}
	}
}

其中authBack函数如下

@Override
public UserInfo authBack(Uri uri, String requestTokenSecret) {
	if (uri == null) {
		return null;
	}

	QqTAppAndToken qqTAppAndToken = new QqTAppAndToken();
	qqTAppAndToken.setAppKey(SnsConstant.QQT_APP_KEY);
	qqTAppAndToken.setAppSecret(SnsConstant.QQT_APP_SECRET);
	qqTSdkService.setQqTAppAndToken(qqTAppAndToken);
	Map<String, String> requestTokenMap = qqTSdkService.getAuthorizedRequestToken(uri.getQuery());
	if (MapUtils.isEmpty(requestTokenMap) || !requestTokenMap.containsKey(QqTConstant.PARA_OAUTH_TOKEN)
		|| !requestTokenMap.containsKey(QqTConstant.PARA_OAUTH_VERIFIER)) {
		return null;
	}
	Map<String, String> accessTokenMap = qqTSdkService.getAccessToken(requestTokenMap.get(QqTConstant.PARA_OAUTH_TOKEN),
																	  requestTokenMap.get(QqTConstant.PARA_OAUTH_VERIFIER),
																	  requestTokenSecret);
	if (!MapUtils.isEmpty(accessTokenMap) || accessTokenMap.containsKey(QqTConstant.PARA_OAUTH_TOKEN)
		|| accessTokenMap.containsKey(QqTConstant.PARA_OAUTH_TOKEN_SECRET)) {
		return UserInfoUtils.createUserInfo(websiteType, null, accessTokenMap.get(QqTConstant.PARA_OAUTH_TOKEN),
											accessTokenMap.get(QqTConstant.PARA_OAUTH_TOKEN_SECRET));
	}

	return null;
}

a. UserInfo类代码如下 

package com.trinea.sns.entity;

import java.io.Serializable;

/**
 * 验证后存储在数据库中的用户信息类
 * 
 * @author Trinea 2012-3-13 上午01:08:30
 */
public class UserInfo implements Serializable {

    private static final long serialVersionUID = -2402890084981532871L;

    /** 用户id,可能对于某些网站类型为空 **/
    private String            userId;
    /** access token **/
    private String            accessToken;
    /** access secret **/
    private String            accessSecret;
    /** 网站类型 **/
    private String            websiteType;
    /** 用户是否已经被选中 **/
    private boolean           isSelected;

    /**
     * 得到用户id,可能对于某些网站类型为空
     * 
     * @return the userId
     */
    public String getUserId() {
        return userId;
    }

    /**
     * 设置用户id
     * 
     * @param userId
     */
    public void setUserId(String userId) {
        this.userId = userId;
    }

    /**
     * 得到accessToken
     * 
     * @return the accessToken
     */
    public String getAccessToken() {
        return accessToken;
    }

    /**
     * 设置accessToken
     * 
     * @param accessToken
     */
    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }

    /**
     * 得到accessSecret
     * 
     * @return the accessSecret
     */
    public String getAccessSecret() {
        return accessSecret;
    }

    /**
     * 设置accessSecret
     * 
     * @param accessSecret
     */
    public void setAccessSecret(String accessSecret) {
        this.accessSecret = accessSecret;
    }

    /**
     * 得到网站类型
     * 
     * @return the websiteType
     */
    public String getWebsiteType() {
        return websiteType;
    }

    /**
     * 设置网站类型
     * 
     * @param websiteType
     */
    public void setWebsiteType(String websiteType) {
        this.websiteType = websiteType;
    }

    /**
     * 设置用户是否已经被选中
     * 
     * @param isSelected
     */
    public void setSelected(boolean isSelected) {
        this.isSelected = isSelected;
    }

    /**
     * 得到用户是否已经被选中
     * 
     * @return the isSelected
     */
    public boolean isSelected() {
        return isSelected;
    }
}

 

b. createUserInfo代码如下

    public static UserInfo createUserInfo(String websiteType, String... userInfo) {
        if (ArrayUtils.isEmpty(userInfo)) {
            return null;
        }

        UserInfo user = new UserInfo();
        user.setUserId((userInfo.length > 0 && userInfo[0] != null) ? userInfo[0] : websiteType);
        user.setAccessToken(userInfo.length > 1 ? userInfo[1] : null);
        user.setAccessSecret((userInfo.length > 2 && userInfo[2] != null) ? userInfo[2] : websiteType);
        user.setWebsiteType(websiteType);
        return user;
    }

到此大功告成,如果想使用腾讯微博android sdk,请见http://trinea.iteye.com/blog/1299505  

 

4、其他

腾讯微博认证异常

向https://open.t.qq.com/cgi-bin/request_token获取未授权的access token出现如下异常

java.lang.Exception: javax.net.ssl.SSLHandshakeException: org.bouncycastle.jce.exception.ExtCertPathValidatorException: Could not validate certificate signature.

原因应该是以上请求的ssl证书已经不可用,将https改为http即可,如http://open.t.qq.com/cgi-bin/request_token

 

分享到:
评论
2 楼 Trinea 2012-07-18  
liyubing1019 写道
没有工程包,案例工程。

抱歉,因为是跟一个私有工程在一起的,暂时还没开源出来
1 楼 liyubing1019 2012-07-18  
没有工程包,案例工程。

相关推荐

    腾讯微博android 客户端 Oauth 认证流程序示例

    本示例主要探讨的是腾讯微博Android客户端OAuth认证流程,这对于任何想要集成腾讯微博服务到Android应用的开发者来说至关重要。 OAuth(开放授权)是一种授权机制,允许第三方应用在用户许可的情况下,安全地访问...

    Java版腾讯微博OAuth2.0授权可发文本微博示例代码

    本示例代码关注的是如何使用Java语言结合腾讯微博的OAuth2.0授权机制来发送文本微博。OAuth2.0是一种广泛采用的开放标准,用于授权第三方应用访问用户的数据,而无需获取用户的用户名和密码。下面我们将深入探讨这个...

    android 腾讯微博Oauth2.0认证以及自定义webview认证

    本话题主要围绕Android应用如何使用OAuth2.0与腾讯微博进行集成,并通过自定义WebView实现用户认证。 OAuth2.0是一种授权框架,它允许第三方应用在用户许可的情况下,访问存储在另一服务上的特定资源。腾讯微博提供...

    新浪、腾讯、网易、搜狐微博的OAuth认证代码示例

    里面的一个项目有4个微博的OAuth代码示例,用Eclipse带ADT插件打开后,必须先把SystemConfig类的相应的常量改成你申请的相应微博的appKey和appSecret才能正常运行,本人亲测其中的腾讯微博部分。

    asp.net 腾讯微博OAuth2.0SDK登陆网站源码

    asp.net 腾讯微博SDK OAuth2.0API接口,示例源码,登陆腾讯微博授权成功后可获得昵称、图像、OpenId,最新微博及粉丝等。100%可用,示例应用于 http://goupiaotong.com/login.aspx

    新浪、腾讯、网易、搜狐微博OAuth认证整合

    6. **OauthTest文件**:这个文件可能是用于测试OAuth整合的代码示例或者工具,可能包含了四大微博平台的OAuth认证实现,开发者可以通过分析和运行这些代码,理解OAuth认证的具体步骤和细节。 在实际开发中,开发者...

    腾讯微博网页版SDK及示例 V1.1源码

    腾讯微博网页版SDK及示例 V1.1源码 配置 1. 微博callback url设置:把web.config里的webroot的值改成你的站点的Default.aspx的实际URL,如 http://t.qq1800.com/demo.aspx 2. Appkey, 改用自己的腾讯微博appkey,把...

    安卓开发-新浪、腾讯、网易、搜狐微博OAuth认证整合Android版.zip.zip

    在压缩包"安卓开发-新浪、腾讯、网易、搜狐微博OAuth认证整合Android版.zip"中,你将找到示例代码、配置文件和相关资源,这些可以帮助你理解并实现上述的OAuth认证过程。通过学习和实践,你将能够为你的Android应用...

    腾讯微博API_SDK_PHP示例

    1. **OAuth认证**:腾讯微博API使用OAuth 2.0进行授权,这是互联网上广泛采用的一种安全授权协议。开发者需要先注册应用获取App Key和App Secret,然后通过OAuth流程获取用户授权,才能访问用户的微博数据。 2. **...

    android腾讯微博分享demo

    "android腾讯微博分享demo"是一个示例项目,旨在帮助开发者理解并实现如何在Android应用中集成腾讯微博的分享功能。这个Demo经过了测试,被认为工作良好,对初学者来说简单易懂。 首先,我们需要了解腾讯微博开放...

    分享到新浪,腾讯微博

    【标题】:“分享到新浪,腾讯微博” 在互联网社交领域,分享内容到新浪和腾讯微博是一种常见的交流方式。这两个平台都是中国领先的社交媒体平台,让用户能够实时发布信息、观点和生活点滴,同时也支持与朋友们互动...

    腾讯微博 API C++-SDK (修正版)

    1. **认证机制**:SDK提供OAuth 2.0认证流程,这是腾讯微博API的基本安全要求。通过授权码(Access Token),开发者可以在用户的许可下访问其微博账户的相关信息。 2. **请求与响应对象**:SDK封装了HTTP请求和响应...

    OAuth 认证流程

    OAuth 认证流程是互联网上广泛采用的一种授权机制,它允许第三方应用在用户授权的情况下,安全地访问其存储在另一服务提供商(如腾讯微博)上的个人信息。OAuth 不涉及用户密码的共享,而是通过一系列步骤来确保用户...

    新浪、腾讯微博,微信好友、朋友圈 分享

    需要注意的是,腾讯微博的分享可能需要处理OAuth 2.0授权流程。 3. 微信分享: 微信提供了微信开放平台,开发者可以注册应用并获取AppID和AppSecret。微信SDK支持多种分享形式,如文本、图片、网页链接、小程序等...

    Java版|腾讯微博|OAuth2.0授权|官方方法|可发文本和图片|示例代码

    在本文中,我们将深入探讨如何使用Java实现腾讯微博的OAuth2.0授权,并结合官方方法发送文本和图片。首先,让我们了解OAuth2.0的基本概念,然后详细讲解腾讯微博的TAPI(Tencent API)以及如何整合这些知识来实现所...

    新浪腾讯微博分享demo

    本篇文章将深入探讨“新浪腾讯微博分享demo”,这是一个专为Android平台设计的示例项目,它实现了通过Oauth2.0协议与新浪微博和腾讯微博的无缝对接,支持文字和图片的分享功能。以下我们将详细解析这个项目的重点...

    android新浪腾讯人人豆瓣等的oauth认证

    OAuth认证是一种开放标准,用于授权第三方应用访问用户在特定服务上的资源,如微博、社交网络或云存储。在Android平台上,开发者需要理解如何实现OAuth来让应用与新浪、腾讯、人人网、豆瓣以及Facebook等社交媒体...

    腾讯微博APIDemo.rar

    腾讯微博API Demo是一款基于腾讯微博开放平台的示例应用,它展示了如何使用腾讯微博的API进行各种功能的实现,如发布微博、获取用户信息、管理粉丝等。此压缩包文件包含了一个完整的SDK(Software Development Kit)...

    ios 腾讯微博接口

    本文将详细介绍如何在iOS项目中实现腾讯微博的分享接口,并探讨其相关知识点。 首先,我们需要了解腾讯微博开放平台(Tencent Open API Platform)提供的SDK,它是实现iOS应用与腾讯微博交互的基础。SDK包含了登录...

    Android版微博OAuth认证整合实例集

    Android版新浪、网易、腾讯、搜狐微博OAuth认证实例集,想更多了解OAuth认证的朋友,请下载java源码包,在示例中完整解析了OAuth认证的过程:定义URL编码方式,请求url,得到注册应用后服务商提供的key和secret,...

Global site tag (gtag.js) - Google Analytics