转载

开放平台-web实现QQ第三方登录

应用场景

web应用通过QQ登录授权实现第三方登录。

操作步骤

1  注册成为QQ互联平台开发者, http://connect.qq.com/

2  准备一个可访问的域名,如dev.foo.com

3  创建网页应用,配置必要信息,其中包括域名以及回调地址;

其中域名需要验证,需确保对域名主机有足够的控制权限

4  获取应用appID、appKey进行开发

登录流程

开发平台的登录授权采取oauth2.0机制,这也是目前几乎所有互联网开放平台所采取的方式。

开放平台-web实现QQ第三方登录

需更多了解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-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的存储,此外对于用户数据(昵称、头像)也做好缓存或持久化,以减少接口的调用频度。

正文到此结束
Loading...