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

相關文章
相關標籤/搜索