• 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"?>
    <ScrollView 
    	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>
    </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"

    @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



    已有 0 人发表留言,猛击->>这里<<-参与讨论


    ITeye推荐



  • 相关阅读:
    爽肤水
    Python面向对象关系
    Linux多线程编程
    Python数据库工具类MySQLdb使用
    Python配置工具类ConfigParser使用
    采用RedisLive监控Redis服务——安装手册
    采用JavaMelody监控Tomcat服务——安装手册
    怎么做性能测试--响应时间
    robot framework测试驱动无法定位页面元素
    使用Loadrunner对IBM MQ进行性能测试
  • 原文地址:https://www.cnblogs.com/trinea/p/2420103.html
Copyright © 2020-2023  润新知