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

本身转载一下本身吧,省得被盗了,贴过来样式都乱了,欢迎你们看原文http://trinea.iteye.com/blog/1290627javascript

 

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

一、OAuth介绍java

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

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

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

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

 

二、OAuth流程安全

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

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

a 先得到一个未受权的request token,或者叫request code

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

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

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

 

三、腾讯微博OAuth api介绍

目前腾讯微博使用的是OAuth1.0、新浪微博使用的是OAuth2.0、人人网使用的是OAuth2.0,这里只介绍腾讯微博,关于人人和新浪相似,你们能够本身修改。

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

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

认证函数以下

Java代码    收藏代码
  1. private static QqTSdkService qqTSdkService = new QqTSdkServiceImpl();  
  2.   
  3. /** 
  4.  * OAuth部分参见http://wiki.open.t.qq.com/index.php/API%E6%96%87%E6%A1%A3#category_1 
  5.  */  
  6. @Override  
  7. public Intent auth(Context context, String callBackUrl) {  
  8.     Intent intent = new Intent();  
  9.     Bundle bundle = new Bundle();  
  10.     QqTAppAndToken qqTAppAndToken = new QqTAppAndToken();  
  11.     qqTAppAndToken.setAppKey(APP_KEY);  
  12.     qqTAppAndToken.setAppSecret(APP_SECRET);  
  13.     qqTSdkService.setQqTAppAndToken(qqTAppAndToken);  
  14.     Map<String, String> requestTokenMap = qqTSdkService.getUnAuthorizedRequestToken(callBackUrl);  
  15.     if (!MapUtils.isEmpty(requestTokenMap) && requestTokenMap.containsKey(QqTConstant.PARA_OAUTH_TOKEN)) {  
  16.         Map<String, String> parasMap = new HashMap<String, String>();  
  17.         parasMap.put(QqTConstant.PARA_OAUTH_TOKEN, requestTokenMap.get(QqTConstant.PARA_OAUTH_TOKEN));  
  18.         bundle.putString(SnsConstant.OAUTH_URL,  
  19.                          HttpUtils.getUrlWithParas(QqTConstant.GET_AUTHORIZATION_URL, parasMap));  
  20.         bundle.putString(SnsConstant.CALL_BACK_URL, callBackUrl);  
  21.         bundle.putString(SnsConstant.REQUEST_TOKEN_SECRET, requestTokenMap.get(SnsConstant.REQUEST_TOKEN_SECRET));  
  22.         intent.putExtras(bundle);  
  23.         intent.setClass(context, OAuthWebViewActivity.class);  
  24.     }  
  25.     return intent;  
  26. }  

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

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

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

Java代码    收藏代码
  1. <intent-filter>  
  2.     <data android:scheme="appName" android:host="activityClassName" />  
  3. </intent-filter>  

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

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

Java代码    收藏代码
  1. /** 程序中用到的一些字符串常量 **/  
  2. public static final String               WEBSITE_TYPE                          = "websiteType";  
  3. public static final String               OAUTH_URL                             = "oAuthUrl";  
  4. public static final String               CALL_BACK_URL                         = "callBackUrl";  
  5. public static final String               REQUEST_TOKEN_SECRET                  = "oauth_token_secret";  
  6. public static final String               STATUS_ID                             = "statusId";  
  7. public static final String               COMMENT_TYPE                          = "commentType";  
  8. public static final String               COMMENT_ID                            = "commentId";  

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

 

activity中调用认证函数

Java代码    收藏代码
  1. Intent intent = auth(context, "appName://activityClassName");  
  2. if (intent == null || intent.getExtras() == null  
  3.     || !intent.getExtras().containsKey(SnsConstant.CALL_BACK_URL)) {  
  4.     // Toast.makeText(this, "进入认证页面失败", Toast.LENGTH_SHORT).show();  
  5.     return;  
  6. else {  
  7.     startActivity(intent);  
  8. }  

 

第二部分:进入认证页面 

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

Java代码    收藏代码
  1. package com.trinea.sns.activity;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.Intent;  
  5. import android.graphics.Bitmap;  
  6. import android.net.Uri;  
  7. import android.net.http.SslError;  
  8. import android.os.Bundle;  
  9. import android.view.KeyEvent;  
  10. import android.view.MotionEvent;  
  11. import android.view.View;  
  12. import android.view.View.OnTouchListener;  
  13. import android.view.Window;  
  14. import android.webkit.SslErrorHandler;  
  15. import android.webkit.WebChromeClient;  
  16. import android.webkit.WebSettings;  
  17. import android.webkit.WebView;  
  18. import android.webkit.WebViewClient;  
  19.   
  20. import com.trinea.sns.util.CodeRules;  
  21. import com.trinea.sns.util.SnsConstant;  
  22.   
  23. /** 
  24.  * 认证的webView 
  25.  *  
  26.  * @author Trinea 2012-3-20 下午08:42:41 
  27.  */  
  28. public class OAuthWebViewActivity extends Activity {  
  29.   
  30.     private WebView                    authWebView = null;  
  31.     private Intent                     intent      = null;  
  32.   
  33.     private String                     callBackUrl;  
  34.     private String                     requestTokenSecret;  
  35.   
  36.     public static OAuthWebViewActivity webInstance = null;  
  37.   
  38.     @Override  
  39.     public void onCreate(Bundle savedInstanceState) {  
  40.         super.onCreate(savedInstanceState);  
  41.         requestWindowFeature(Window.FEATURE_PROGRESS);  
  42.         setContentView(R.layout.web_view);  
  43.         setTitle("腾讯微博受权认证");  
  44.   
  45.         webInstance = this;  
  46.         authWebView = (WebView)findViewById(R.id.authWebView);  
  47.         WebSettings webSettings = authWebView.getSettings();  
  48.         webSettings.setJavaScriptEnabled(true);  
  49.         webSettings.setSaveFormData(true);  
  50.         webSettings.setSavePassword(true);  
  51.         webSettings.setSupportZoom(true);  
  52.         webSettings.setBuiltInZoomControls(true);  
  53.         webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);  
  54.   
  55.         authWebView.setOnTouchListener(new OnTouchListener() {  
  56.   
  57.             @Override  
  58.             public boolean onTouch(View v, MotionEvent event) {  
  59.                 authWebView.requestFocus();  
  60.                 return false;  
  61.             }  
  62.         });  
  63.   
  64.         // 根据传递过来的信息,打开相应的受权页面  
  65.         intent = this.getIntent();  
  66.         if (!intent.equals(null)) {  
  67.             Bundle bundle = intent.getExtras();  
  68.             if (bundle != null && bundle.containsKey(SnsConstant.OAUTH_URL)) {  
  69.                 authWebView.loadUrl(bundle.getString(SnsConstant.OAUTH_URL));  
  70.                 if (bundle.getString(SnsConstant.CALL_BACK_URL) != null) {  
  71.                     callBackUrl = bundle.getString(SnsConstant.CALL_BACK_URL);  
  72.                 }  
  73.                 if (bundle.getString(SnsConstant.REQUEST_TOKEN_SECRET) != null) {  
  74.                     requestTokenSecret = bundle.getString(SnsConstant.REQUEST_TOKEN_SECRET);  
  75.                 }  
  76.                 authWebView.setWebChromeClient(new WebChromeClient() {  
  77.   
  78.                     public void onProgressChanged(WebView view, int progress) {  
  79.                         setTitle("腾讯微博受权页面加载中,请稍候..." + progress + "%");  
  80.                         setProgress(progress * 100);  
  81.   
  82.                         if (progress == 100) {  
  83.                             setTitle(R.string.app_name);  
  84.                         }  
  85.                     }  
  86.                 });  
  87.   
  88.                 authWebView.setWebViewClient(new WebViewClient() {  
  89.   
  90.                     @Override  
  91.                     public boolean shouldOverrideUrlLoading(WebView view, String url) {  
  92.                         view.loadUrl(url);  
  93.                         return true;  
  94.                     }  
  95.   
  96.                     @Override  
  97.                     public void onPageStarted(WebView webView, String url, Bitmap favicon) {  
  98.                         if (url != null && url.startsWith(callBackUrl)) {  
  99.                             Class backClass = CodeRules.getActivityClass(CodeRules.getActivityNameFromUrl(callBackUrl));  
  100.                             if (backClass != null) {  
  101.                                 Intent intent = new Intent(OAuthWebViewActivity.this, backClass);  
  102.                                 Bundle backBundle = new Bundle();  
  103.                                 backBundle.putString(SnsConstant.REQUEST_TOKEN_SECRET, requestTokenSecret);  
  104.                                 intent.putExtras(backBundle);  
  105.                                 Uri uri = Uri.parse(url);  
  106.                                 intent.setData(uri);  
  107.                                 startActivity(intent);  
  108.                             }  
  109.                         }  
  110.                     }  
  111.                 });  
  112.             }  
  113.         }  
  114.     }  
  115.   
  116.     @Override  
  117.     protected void onPause() {  
  118.         super.onPause();  
  119.     }  
  120.   
  121.     @Override  
  122.     protected void onResume() {  
  123.         super.onResume();  
  124.     }  
  125.   
  126.     @Override  
  127.     protected void onStop() {  
  128.         super.onStop();  
  129.     }  
  130.   
  131.     @Override  
  132.     protected void onDestroy() {  
  133.         super.onDestroy();  
  134.     }  
  135.   
  136.     /** 
  137.      * 监听BACK键 
  138.      *  
  139.      * @param keyCode 
  140.      * @param event 
  141.      * @return 
  142.      */  
  143.     @Override  
  144.     public boolean onKeyDown(int keyCode, KeyEvent event) {  
  145.         if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {  
  146.             if (authWebView.canGoBack()) {  
  147.                 authWebView.goBack();  
  148.             } else {  
  149.                 // OAuthActivity.webInstance.finish();  
  150.                 finish();  
  151.             }  
  152.             return true;  
  153.         }  
  154.   
  155.         return super.onKeyDown(keyCode, event);  
  156.     }  
  157. }  

a. R.layout.web_view为

Xml代码    收藏代码
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <ScrollView   
  3.     xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:orientation="vertical">  
  7.       <WebView     
  8.         android:layout_height="wrap_content"  
  9.         android:layout_width="wrap_content"  
  10.         android:id="@+id/authWebView">  
  11.       </WebView>  
  12. </ScrollView>  

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"

Java代码    收藏代码
  1. @Override  
  2. protected void onNewIntent(Intent intent) {  
  3.     super.onNewIntent(intent);  
  4.   
  5.     Bundle bundle = intent.getExtras();  
  6.     if (bundle != null) {  
  7.         UserInfo userInfo = authBack(intent.getData(), bundle.getString(SnsConstant.REQUEST_TOKEN_SECRET));  
  8.         if (userInfo != null) {  
  9.             Toast.makeText(this"获取用户信息失败,请从新验证", Toast.LENGTH_SHORT).show();  
  10.             OAuthWebViewActivity.webInstance.finish();  
  11.         } else {  
  12.             Toast.makeText(this"获取用户信息失败,请从新验证", Toast.LENGTH_SHORT).show();  
  13.         }  
  14.     }  
  15. }  

其中authBack函数以下

Java代码    收藏代码
  1. @Override  
  2. public UserInfo authBack(Uri uri, String requestTokenSecret) {  
  3.     if (uri == null) {  
  4.         return null;  
  5.     }  
  6.   
  7.     QqTAppAndToken qqTAppAndToken = new QqTAppAndToken();  
  8.     qqTAppAndToken.setAppKey(SnsConstant.QQT_APP_KEY);  
  9.     qqTAppAndToken.setAppSecret(SnsConstant.QQT_APP_SECRET);  
  10.     qqTSdkService.setQqTAppAndToken(qqTAppAndToken);  
  11.     Map<String, String> requestTokenMap = qqTSdkService.getAuthorizedRequestToken(uri.getQuery());  
  12.     if (MapUtils.isEmpty(requestTokenMap) || !requestTokenMap.containsKey(QqTConstant.PARA_OAUTH_TOKEN)  
  13.         || !requestTokenMap.containsKey(QqTConstant.PARA_OAUTH_VERIFIER)) {  
  14.         return null;  
  15.     }  
  16.     Map<String, String> accessTokenMap = qqTSdkService.getAccessToken(requestTokenMap.get(QqTConstant.PARA_OAUTH_TOKEN),  
  17.                                                                       requestTokenMap.get(QqTConstant.PARA_OAUTH_VERIFIER),  
  18.                                                                       requestTokenSecret);  
  19.     if (!MapUtils.isEmpty(accessTokenMap) || accessTokenMap.containsKey(QqTConstant.PARA_OAUTH_TOKEN)  
  20.         || accessTokenMap.containsKey(QqTConstant.PARA_OAUTH_TOKEN_SECRET)) {  
  21.         return UserInfoUtils.createUserInfo(websiteType, null, accessTokenMap.get(QqTConstant.PARA_OAUTH_TOKEN),  
  22.                                             accessTokenMap.get(QqTConstant.PARA_OAUTH_TOKEN_SECRET));  
  23.     }  
  24.   
  25.     return null;  
  26. }  

a. UserInfo类代码以下 

Java代码    收藏代码
  1. package com.trinea.sns.entity;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. /** 
  6.  * 验证后存储在数据库中的用户信息类 
  7.  *  
  8.  * @author Trinea 2012-3-13 上午01:08:30 
  9.  */  
  10. public class UserInfo implements Serializable {  
  11.   
  12.     private static final long serialVersionUID = -2402890084981532871L;  
  13.   
  14.     /** 用户id,可能对于某些网站类型为空 **/  
  15.     private String            userId;  
  16.     /** access token **/  
  17.     private String            accessToken;  
  18.     /** access secret **/  
  19.     private String            accessSecret;  
  20.     /** 网站类型 **/  
  21.     private String            websiteType;  
  22.     /** 用户是否已经被选中 **/  
  23.     private boolean           isSelected;  
  24.   
  25.     /** 
  26.      * 获得用户id,可能对于某些网站类型为空 
  27.      *  
  28.      * @return the userId 
  29.      */  
  30.     public String getUserId() {  
  31.         return userId;  
  32.     }  
  33.   
  34.     /** 
  35.      * 设置用户id 
  36.      *  
  37.      * @param userId 
  38.      */  
  39.     public void setUserId(String userId) {  
  40.         this.userId = userId;  
  41.     }  
  42.   
  43.     /** 
  44.      * 获得accessToken 
  45.      *  
  46.      * @return the accessToken 
  47.      */  
  48.     public String getAccessToken() {  
  49.         return accessToken;  
  50.     }  
  51.   
  52.     /** 
  53.      * 设置accessToken 
  54.      *  
  55.      * @param accessToken 
  56.      */  
  57.     public void setAccessToken(String accessToken) {  
  58.         this.accessToken = accessToken;  
  59.     }  
  60.   
  61.     /** 
  62.      * 获得accessSecret 
  63.      *  
  64.      * @return the accessSecret 
  65.      */  
  66.     public String getAccessSecret() {  
  67.         return accessSecret;  
  68.     }  
  69.   
  70.     /** 
  71.      * 设置accessSecret 
  72.      *  
  73.      * @param accessSecret 
  74.      */  
  75.     public void setAccessSecret(String accessSecret) {  
  76.         this.accessSecret = accessSecret;  
  77.     }  
  78.   
  79.     /** 
  80.      * 获得网站类型 
  81.      *  
  82.      * @return the websiteType 
  83.      */  
  84.     public String getWebsiteType() {  
  85.         return websiteType;  
  86.     }  
  87.   
  88.     /** 
  89.      * 设置网站类型 
  90.      *  
  91.      * @param websiteType 
  92.      */  
  93.     public void setWebsiteType(String websiteType) {  
  94.         this.websiteType = websiteType;  
  95.     }  
  96.   
  97.     /** 
  98.      * 设置用户是否已经被选中 
  99.      *  
  100.      * @param isSelected 
  101.      */  
  102.     public void setSelected(boolean isSelected) {  
  103.         this.isSelected = isSelected;  
  104.     }  
  105.   
  106.     /** 
  107.      * 获得用户是否已经被选中 
  108.      *  
  109.      * @return the isSelected 
  110.      */  
  111.     public boolean isSelected() {  
  112.         return isSelected;  
  113.     }  
  114. }  

 

b. createUserInfo代码以下

Java代码    收藏代码
  1. public static UserInfo createUserInfo(String websiteType, String... userInfo) {  
  2.     if (ArrayUtils.isEmpty(userInfo)) {  
  3.         return null;  
  4.     }  
  5.   
  6.     UserInfo user = new UserInfo();  
  7.     user.setUserId((userInfo.length > 0 && userInfo[0] != null) ? userInfo[0] : websiteType);  
  8.     user.setAccessToken(userInfo.length > 1 ? userInfo[1] : null);  
  9.     user.setAccessSecret((userInfo.length > 2 && userInfo[2] != null) ? userInfo[2] : websiteType);  
  10.     user.setWebsiteType(websiteType);  
  11.     return user;  
  12. }  

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

 

四、其余

腾讯微博认证异常

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

Java代码    收藏代码
  1. 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

相关文章
相关标签/搜索