應用場景
web應用通過QQ登入授權實作第三方登入。
操作步驟
1 注冊成為QQ互聯平台開發者,
http://connect.qq.com/2 準備一個可通路的域名,如dev.foo.com
3 建立網頁應用,配置必要資訊,其中包括域名以及回調位址;
其中域名需要驗證,需確定對域名主機有足夠的控制權限
4 擷取應用appID、appKey進行開發
登入流程
開發平台的登入授權采取oauth2.0機制,這也是目前幾乎所有網際網路開放平台所采取的方式。

需更多了解oauth2.0可參考阮老師的文章:
http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html實作方式
client-side
流程:
前端頁面通過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-sideSDK使用
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的存儲,此外對于使用者資料(昵稱、頭像)也做好緩存或持久化,以減少接口的調用頻度。
作者:
zale出處:
http://www.cnblogs.com/littleatp/, 如果喜歡我的文章,請
關注我的公衆号本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出
原文連結如有問題, 可留言咨詢.