uid
,QQ是加密的openId
)以及用來識別身份的accessToken
,當然還有暱稱、頭像、性別等有限資料,對接第三方登錄的關鍵就是如何確定用戶是合法登錄,如果確定這次登錄的和上次登錄的是同一個人並且不是假冒的。accessToken
,如果這個code是用戶亂填的,那這一關肯定過不了,所以,前面的擔心有點多餘。OpenUser
表關聯起來,判斷用戶是否登錄時把OpenUser
的鑑權也加進去就OK了。2.1. 數據庫設計
OpenUser
表用來存放第三方登錄用戶,主要字段如下:2.2. 鑑權流程
accessToken
寫入cookie肯定是不安全的,因爲accessToken
相當於是第三方網站的臨時密碼,被別人竊取了就可以隨意拿來幹壞事了。可以在用戶登錄成功之後我們自己生成一個token,這樣的token即使泄露了頂多就是被人拿來隨意評論,損失不大,但是如果accessToken被泄露了,以微博爲例,人家可以利用這個accessToken
隨意發微博、刪微博、加關注等等,很危險。當然,如果不想token泄露的話也可以通過綁定IP等方式來限制。3.1. 實名認證
3.2. 創建應用
appId
和appKey
。3.3. 引導用戶登錄
function openWindow(url, width, height)
{
width = width || 600;
height = height || 400;
var left = (window.screen.width - width) / 2;
var top = (window.screen.height - height) / 2;
window.open(url, "_blank", "toolbar=yes, location=yes, directories=no, status=no, menubar=yes, scrollbars=yes, resizable=no, copyhistory=yes, left="+left+", top="+top+", width="+width+", height="+height);
}
function qqLogin()
{
var qqAppId = '424323422'; // 上面申請得到的appid
var qqAuthPath = 'http://www.test.com/auth'; // 前面設置的回調地址
var state = 'fjdslfjsdlkfd'; // 防止CSRF攻擊的隨機參數,必傳,登錄成功之後會回傳,最好後臺自己生成然後校驗合法性
openWindow(`https://graph.qq.com/oauth2.0/authorize?response_type=token&client_id=${qqAppId}&redirect_uri=${encodeURIComponent(qqAuthPath)}&state=${state}`);
}
3.4. 拿到accessToken
#
後面,URL地址中的hash值好像不會被傳到後臺(貌似是這樣,如有不正確歡迎評論指正),所以只能寫一個下面這樣的臨時頁面:@RequestMapping("/authqq")
public void authQQ(HttpServletRequest request, HttpServletResponse response) throws Exception
{
// QQ登錄有點特殊,參數放在#後面,後臺無法獲取#後面的參數,只能用JS做中間轉換
String html = "<!DOCTYPE html>" +
"<html lang=\"zh-cn\">" +
"<head>" +
" <title>QQ登錄重定向頁</title>" +
" <meta charset=\"utf-8\"/>" +
"</head>" +
"<body>" +
" <script type=\"text/javascript\">" +
" location.href = location.href.replace('#', '&').replace('auth_qq', 'auth_qq_redirect');" +
"
</script>
" +
"</body>" +
"</html>";
response.getWriter().print(html);
}
QQ號+appId
唯一的,換句話說同一個QQ號登錄2個不同appId時獲取到的openId是不同的。順便說一句,QQ登錄的相關接口做的還真夠“隨便”的,全部都是最簡單的get請求,所以對接起來非常順利。 微信搜索 Web項目聚集地 獲取更多實戰教程。// 根據accessToken換取openId
// 錯誤示例:callback( {"error":100016,"error_description":"access token check failed"} );
// 正確示例:callback( {"client_id":"10XXXXX49","openid":"CF2XXXXXXXX9F4C"} );
String result = HttpsUtil.get("https://graph.qq.com/oauth2.0/me?access_token=" + accessToken);
Map<String, Object> resp = parseQQAuthResponse(result); // 這個方法就是把結果轉Map
// 歡迎關注 Web項目聚集地 獲取更多實戰教程
Integer errorCode = (Integer)resp.get("error");
String errorMsg = (String)resp.get("error_description");
String openId = (String)resp.get("openid");
if(errorCode != null) return new ErrorResult(errorCode, "獲取QQ用戶openId失敗:"+errorMsg);
// 獲取用戶暱稱、頭像等信息,{ret: 0, msg: '', nickname: '', ...} ret不爲0表示失敗
result = HttpsUtil.get("https://graph.qq.com/user/get_user_info?access_token="+accessToken+"&oauth_consumer_key="+appId+"&openid="+openId);
resp = JsonUtil.parseJsonToMap(result);
// 歡迎關注 Web項目聚集地 獲取更多實戰教程
Integer ret = (Integer)resp.get("ret");
String msg = (String)resp.get("msg");
if(ret != 0) return new ErrorResult("獲取用戶QQ信息失敗:"+msg);
// 用戶暱稱可能存在4個字節的utf-8字符,MySQL默認不支持,直接插入會報錯,所以過濾掉
String nickname = StringUtil.filterUtf8Mb4((String)resp.get("nickname")).trim(); // 這個方法可以自行百度
// figureurl_qq_2=QQ的100*100頭像,figureurl_2=QQ 100&100空間頭像,QQ頭像不一定有,空間頭像一定有
String avatar = (String)resp.get("figureurl_qq_2");
if(StringUtil.isBlank(avatar)) avatar = (String)resp.get("figureurl_2");
String gender = (String)resp.get("gender");
- 需要注意數據庫中是否已經有改用戶,沒有的添加,有的修改,不要重複添加了;
- QQ暱稱暱稱有各種奇奇怪怪的字符,包括emoji,MySQL默認沒有開啓
utf8mb4
,直接插入會報錯,所以需要過濾掉; - 需要做好對各種錯誤的兼容;
- 接口會同時返回QQ頭像和空間頭像,QQ頭像不一定有,空間頭像一定有;
- 回調地址必須和申請的域名一致,否則會報錯。
- QQ互聯有個特大的bug,有時候顯示已登錄但是點擊授權管理一直報錯,此時只需要退出重新登錄即可;
- 授權之後用戶可能會在過期之前提前取消授權;
- 微信搜索 Web項目聚集地 獲取更多實戰教程。
對接微博登陸
4.1. 實名認證
4.2. 創建應用
有關微博的對接可以參考我好幾年前寫的一篇文章:
http://www.cnblogs.com/liuxianan/archive/2012/11/11/2765123.html
4.3. 引導用戶登錄
function weiboLogin()
{
let weiboAppId = '432432';
let weiboAuthPath = 'http://www.test.com/authweibo';
openWindow(`https://api.weibo.com/oauth2/authorize?client_id=${weiboAppId}&response_type=code&redirect_uri=${encodeURIComponent(weiboAuthPath)}`);
}
4.4. 獲取accessToken
String params = "client_id=" + appId
+ "&client_secret=" + appSecret
+ "&grant_type=authorization_code"
+ "&redirect_uri=" + URLUtil.encode(authPath)
+ "&code=" + code;
// 用code換取accessToken
String result = HttpsUtil.post("https://api.weibo.com/oauth2/access_token", params);
Map<String, Object> resp = JsonUtil.toObject(result, new TypeReference<Map<String, Object>>(){});
Integer errorCode = (Integer)resp.get("error_code");
String error = (String)resp.get("error");
String errorMsg = (String)resp.get("error_description");
if(errorCode != null && errorCode != 0) return new ErrorResult(errorCode, error + (errorMsg==null?"":errorMsg));
String accessToken = (String)resp.get("access_token");
String uid = (String)resp.get("uid"); // 這個uid就是微博用戶的唯一用戶ID,可以通過這個id直接訪問到用戶微博主頁
int expires = (Integer)resp.get("expires_in"); // 有效期,單位秒
4.5. 獲取用戶頭像等信息
- 微博接口都有頻率限制,不過一般不會超過;
- 需做好錯誤兼容;
- 微博直接返回的uid,可以根據這個uid直達用戶微博主頁 https://weibo.com/u/xxxxx ,所以可以把用戶頭像鏈接到這裏;
- 其實也有現成的js-sdk,可以根據自己實際需要選擇是否使用;
- 微博的接口是https,並且是post,需要注意;
微博開放平臺:open.weibo.com/
- 微博登錄授權機制:open.weibo.com/wiki/授權機制
- QQ互聯:connect.qq.com/
- QQ授權管理頁面:connect.qq.com/manage.html#/appauth/user
END
推薦閱讀:
《
《
《
《
《