開放平臺-web實現QQ第三方登陸

應用場景

    web應用經過QQ登陸受權實現第三方登陸。javascript

 

操做步驟

    1  註冊成爲QQ互聯平臺開發者,http://connect.qq.com/html

    2  準備一個可訪問的域名,如dev.foo.com前端

    3  建立網頁應用,配置必要信息,其中包括域名以及回調地址;java

        其中域名須要驗證,需確保對域名主機有足夠的控制權限jquery

    4  獲取應用appID、appKey進行開發web

 

登陸流程

    開發平臺的登陸受權採起oauth2.0機制,這也是目前幾乎全部互聯網開放平臺所採起的方式。json

 

    

 

    需更多瞭解oauth2.0可參考阮老師的文章: http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.htmlwindows

 

實現方式

    client-side後端

        流程:api

        前端頁面經過Implict方式 登陸受權 -> 回調得到accessToken -> 獲取openid -> 同步用戶信息並登陸

 

        爲了保證數據安全,在獲取用戶信息並登陸這一步必須由服務端實現

        這種方式的開發相對便捷,也是後面的實戰案例將要採起的方式。

 

    server-side

        流程:

        由server端頁面跳轉到登陸受權頁面(Authorization code方式) -> 回調得到code -> 置換accessToken -> 獲取openid -> 同步用戶信息並登陸

        可參考:http://wiki.connect.qq.com/%E5%BC%80%E5%8F%91%E6%94%BB%E7%95%A5_server-side

 

SDK使用

    JSSDK  可快捷實現前端登陸受權的功能,可自定製登陸按鈕

        使用文檔:http://wiki.connect.qq.com/js_sdk%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E

        缺點:存在瀏覽器兼容風險,此外登陸按鈕UI的定製也存在受限

 

    JavaSDK  屏蔽了oauth受權的複雜度,方便後端實現受權及api操做

        缺點:增長依賴jar包,項目容易變得臃腫,尤爲是當前項目已經存在oauth功能實現時可沒必要採用

 

 

案例實戰

 

    功能描述

    clientside + server-side 經過QQ網頁受權登陸,並獲取用戶信息

 

    1  本地開發環境準備

        修改hosts文件將dev.foo.com映射到127.0.0.1;

        本地服務器以80端口啓動, windows下可能會出現80端口被系統進程佔用的狀況,解決方法可參考 http://www.cnblogs.com/littleatp/p/4414578.html

        本地服務器啓動後,以dev.foo.com的域名進行訪問,在QQ登陸受權時可經過域名驗證這一步

 

    2  登陸跳轉頁面

<html>
     <head>
        <title>QQ登陸跳轉</title>
        <script src="http://lib.sinaapp.com/js/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
        
        <script type="text/javascript">
        
        //切割字符串轉換參數表
        function toParamMap(str){
      	   var map = {};
      	   var segs = str.split("&");
      	   for(var i in segs){
      		   var seg = segs[i];
      		   var idx = seg.indexOf('=');
      		   if(idx < 0){
      			   continue;
      		   }
      		   var name = seg.substring(0, idx);
      		   var value = seg.substring(idx+1);
      		   map[name] = value;
      	   }
      	   return map;
         }
        
        //隱式獲取url響應內容(JSONP)
        function openImplict(url){
     	   var script = document.createElement('script');
            script.src = url;
            document.body.appendChild(script);        
        }
        
        //得到openid的回調
        function callback(obj)
        {
     	  var openid = obj.openid;
     	  $("#openid").text(openid);
     	  
     	  //跳轉服務端登陸url
     	  var resulturl = "@{openapi.QQs.login_result()}"; 
     	  var accessToken = $("#accessToken").text();
     	  
     	  //向服務端傳輸access_token及openid參數
     	  document.location.href=resulturl + "?access_token=" + accessToken + "&openid=" + openid;
        }
        
        
        </script>
     </head>
     
     <body>
            <p>AccessToken:<span id="accessToken"></span>--ExpireIn<span id="expire"></span></p>
            <p>OpenID:<span id="openid"></span></p>
     
     <!-- 執行腳本 -->
     <script type="text/javascript">
     
     //應用的APPID
     var appID = "101207268";
     
     //登陸受權後的回調地址,設置爲當前url
     var redirectURI = "@@{openapi.QQs.login()}";

     //初始構造請求
     if (window.location.hash.length == 0)
     {
        var path = 'https://graph.qq.com/oauth2.0/authorize?';
        var queryParams = ['client_id=' + appID,
                           'redirect_uri=' + redirectURI,
                           'scope=' + 'get_user_info,list_album,upload_pic,add_feeds,do_like','response_type=token'];

        var query = queryParams.join('&');
        var url = path + query;
        window.location.href= url;
     }
     //在成功受權後回調時location.hash將帶有access_token信息,開始獲取openid
     else
     {
        //獲取access token
        var accessToken = window.location.hash.substring(1);
        var map = toParamMap(accessToken);
        
        //記錄accessToken
        $("#accessToken").text(map.access_token);
        $("#expire").text(map.expires_in);
        
        //使用Access Token來獲取用戶的OpenID
        var path = "https://graph.qq.com/oauth2.0/me?";
        var queryParams = ['access_token='+map.access_token, 'callback=callback'];
        var query = queryParams.join('&');
        var url = path + query;
        openImplict(url);
     }
     
     </script>
     </body>
</html>

        功能描述

        頁面在第一次打開時跳轉到QQ登陸受權頁面;

        受權成功以後回到當前頁面經過url參數(hash串)得到accessToken;

        此後可經過jsonp方式獲取用戶的openid,url如:

https://graph.qq.com/oauth2.0/me?access_token=YOUR_ACCESS_TOKEN

        獲取到用戶OpenID,返回包以下(JSONP方式獲取):

callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} )

         將access_token及openid傳到服務端進行處理

 

   3  server端獲取用戶信息

     接收openid的頁面方法

      /**
     * 登陸結果
     *
     * @param access_token
     * @param openid     */
    public static void login_result(String access_token, String openid) {
        //調用api獲取qq用戶信息
        QQUserInfo user = QQApi.getUserInfo(access_token, openid); 
        
        //此時若取得user信息,則能夠進行保存,並執行用戶登陸操做
        ....        
        //登陸成功後跳轉        
        redirect(xxx);
    }

 

    QQApi的實現 

/**
 * QQ互聯API
 * 
 * <pre>
 * 登陸流程:
 * 
 * 1 前端跳轉qq受權頁面
 * 2 js得到access_token
 * 3 經過jsonp方式得到openid
 * 4 server端根據上傳的access_token及openid獲取用戶信息,如暱稱、頭像
 * 
 * 參考文檔:
 * http://wiki.connect.qq.com/%E5%BC%80%E5%8F%91%E6%94%BB%E7%95%A5_client-side#Step2.EF.BC.9A.E8.8E.B7.E5.8F.96AccessToken
 * </pre>
 * 
 * @author xxx
 * @createDate 2015年3月10日
 * 
 */
public class QQApi {

    public static String appId = "xxx";
    public static String appSecret = "xxx";

    public static String baseUrl = "https://graph.qq.com";

    protected static final String URL_GET_USERINFO = baseUrl
            + "/user/get_user_info?access_token=%s&oauth_consumer_key=%s&openid=%s";

    protected static final long ACCESS_TIMEOUT = 15;

    protected static final String DEF_APP_TOKEN_EXPIRE = "3h";

    /**
     * 獲取用戶信息
     * 
     * <pre>
     * http://wiki.connect.qq.com/get_user_info
     * 
     * 
     * 調用地址:
     * https://graph.qq.com/user/get_user_info
     * 參數
     *   access_token=*************&
     *   oauth_consumer_key=12345&
     *   openid
     * 
     * 返回結果以下:
     * {
     *     "ret": 0,
     *     "msg": "",
     *     "is_lost": 0,
     *     "nickname": "小吞",
     *     "gender": "女",
     *     "province": "廣東",
     *     "city": "廣州",
     *     "year": "1993",
     *     "figureurl": "http://qzapp.qlogo.cn/qzapp/101207268/982C9FEADAF7B242C5069B8F390784BF/30",
     *     "figureurl_1": "http://qzapp.qlogo.cn/qzapp/101207268/982C9FEADAF7B242C5069B8F390784BF/50",
     *     "figureurl_2": "http://qzapp.qlogo.cn/qzapp/101207268/982C9FEADAF7B242C5069B8F390784BF/100",
     *     "figureurl_qq_1": "http://q.qlogo.cn/qqapp/101207268/982C9FEADAF7B242C5069B8F390784BF/40",
     *     "figureurl_qq_2": "http://q.qlogo.cn/qqapp/101207268/982C9FEADAF7B242C5069B8F390784BF/100",
     *     "is_yellow_vip": "0",
     *     "vip": "0",
     *     "yellow_vip_level": "0",
     *     "level": "0",
     *     "is_yellow_year_vip": "0"
     * }
     * </pre>
     * 
     * @param accessToken
     * @return
     */
    public static QQUserInfo getUserInfo(String accessToken, String openid) {
        if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(openid)) {
            return null;
        }

        String url = String.format(URL_GET_USERINFO, accessToken, appId, openid);

        String resultString = DefaultHttp.get(url, ACCESS_TIMEOUT, GlobalConstants.UTF_8);

        Logger.debug("[sso-qq]get userinfo. use url '%s'", url);

        QQUserInfo userinfo = JsonUtil.fromJson(resultString, QQUserInfo.class);
        if (userinfo == null || !userinfo.hasGot()) {
            Logger.debug("[sso-qq]get userinfo failed, with result of '%s'", resultString);
            return null;
        }

        Logger.debug("[sso-qq]get userinfo success, with result of '%s'", resultString);
        return userinfo;
    }

 

常見問題

 

網頁跳轉提示 "redirect_uri_mismatch"

        一般是應用配置中的域名與當前開發服務器訪問地址不一致致使,參照案例中的本地開發環境準備小節

 

api調用返回錯誤

        查看返回的ret字段,對於非0值的ret則表示異常結果,可經過如下地址查詢錯誤緣由:

        http://wiki.connect.qq.com/%E5%85%AC%E5%85%B1%E8%BF%94%E5%9B%9E%E7%A0%81%E8%AF%B4%E6%98%8E

 

接口調用過於頻繁或超過限制

        應用系統可作好access_token的存儲,此外對於用戶數據(暱稱、頭像)也作好緩存或持久化,以減小接口的調用頻度。

相關文章
相關標籤/搜索