文档章节

QQ登录互联SDK详解 & 优化.

Tuesday
 Tuesday
发布于 2013/12/29 22:09
字数 1266
阅读 3945
收藏 30
点赞 2
评论 6

2013-12-26日, 腾讯更新了QQ登录组件2.1, 可以见官方下载链接: QQ click, 重点就是修复一重要的安全问题, 并且在邮件中群发了此消息, 相信许多用户会着手升级与调试. SDK下载后, 就开始表达腾讯的马虎了. 
   
官方md5:1A180F106EF785AE4A04E91EA1882A69
我下载了十多次, 检测md5: D34936AFE7694070EE493B890392C58E


特别是文件md5值, 我们见腾讯的游戏下载, 重要软件都会公布md5值, 防止网络运营商中转离线资源, 可这php sdk我失望了, 下载的包, 跟官方描述的md5是不符合的, 这是描述未更新过来的情况? 对于文件md5值, 个人也把意见提交给QQ旋风, 这是非常重要的. 希望腾讯要认真了.


解压到文件夹后, 发现这版本似乎做了一个demo版本出来, 给普通用户一个直观的界面操作. 如:


这是北大青鸟用户元旦PHP作业吗? 好吧, 腾讯你伤了我的心.


当没有勾选授权列表项目时, 会提示如下错误:

Warning: implode(): Invalid arguments passed in D:\xampps\htdocs\Connect2.1\install\index.php on line 29

所以我觉得input type应该是radio才对, 一定要选择一个项目.



核心文件为API目录下的qqConnectAPI.php, 所有调试均会经过这个php, 共有三行有用代码, 包括开始session, 引入配置文件, 引入QC核心库. 或许官方不想考虑更深层的整合问题, 我觉得在这儿开启session是有问题, 这使得流程更加不好控制.



class目录下有四个文件:
  ErrorCase.class.php (错误类), Recorder.class.php (用户配置类), URL.class.php (url类库存), Oauth.class.php(验证类) , QC.class.php(调试类, 中转功能)



Recorder.class.php 文件初始化方法:

public function __construct(){
        $this->error = new ErrorCase();

        //-------读取配置文件
        $incFileContents = file(ROOT."comm/inc.php");
        $incFileContents = $incFileContents[1];
        $this->inc = json_decode($incFileContents);
        if(empty($this->inc)){
            $this->error->showError("20001");
        }

        if(empty($_SESSION['QC_userData'])){
            self::$data = array();
        }else{
            self::$data = $_SESSION['QC_userData'];
        }
    }


当你配置了appid, appkey后, 就会产生一个inc.php文件, 里面就你的一些配置, 用的是json格式, 我惊呆. 通过这儿,释放进对象, 个人觉得这个类完全没多少意义. 



const VERSION = "2.0";



当我看到这一行代码时, 我终于明白为什么腾讯需要"360护航"了, 亲, 你到底是2.0还是2.1?

QQ互联何需如此复杂, 下面提供我优化的单一php互联版. 方便大家集成进系统.

类库文件代码:
<?php
/* PHP SDK
 * @version 2.1
 * @author connect@qq.com
 * @copyright © 2013, Tencent Corporation. All rights reserved.
 */

class Oauth{
    
    const VERSION = "2.1";
    const GET_AUTH_CODE_URL = "https://graph.qq.com/oauth2.0/authorize";
    const GET_ACCESS_TOKEN_URL = "https://graph.qq.com/oauth2.0/token";
    const GET_OPENID_URL = "https://graph.qq.com/oauth2.0/me";

    public $appid;
    public $appkey;
    public $appcallback;
    public $apperrreport;
    public $appscope;
    
    public $callback_code;
    
    function __construct(){
        $this->appid = QQ_APPID;
        $this->appkey = QQ_APPKEY;
        $this->appcallback = QQ_CALLBACK;
        $this->apperrreport = QQ_ERRORREPORT;
        $this->appscope = QQ_SCOPE;
        
        if(!$this->appid || !$this->appkey || !$this->appcallback)
            $this->showError('20001');
        
        $this->callback_state = $_GET['state'];
    }

    public function qq_login(){
        
        //-------生成唯一随机串防CSRF攻击
        $state = md5(uniqid(rand(), true));
        
        $this->qq_static_var('state',$state);

        //-------构造请求参数列表
        $keysArr = array(
            "response_type" => "code",
            "client_id" => $this->appid,
            "redirect_uri" => urlencode($this->appcallback),
            "state" => $state,
            "scope" => $this->appscope,
        );

        $login_url = $this->combineURL(self::GET_AUTH_CODE_URL, $keysArr);
        
        // 直接跳走
        header("Location:$login_url");
        exit();
    }

    public function qq_callback(){
        $state = $this->qq_static_var('state');
        $code  = $_REQUEST['code'];
        
        //--------验证state防止CSRF攻击
        if($this->callback_state !== $state){
            $this->showError("30001");
        }

        //-------请求参数列表
        $keysArr = array(
            "grant_type" => "authorization_code",
            "client_id" => $this->appid,
            "redirect_uri" => urlencode($this->appcallback),
            "client_secret" => $this->appkey,
            "code" =>$code,
        );

        //------构造请求access_token的url
        $token_url = $this->combineURL(self::GET_ACCESS_TOKEN_URL, $keysArr);
        $response = $this->get_contents($token_url);
        
        if(strpos($response, "callback") !== false){
            $lpos = strpos($response, "(");
            $rpos = strrpos($response, ")");
            $response  = substr($response, $lpos + 1, $rpos - $lpos -1);
            $msg = json_decode($response);

            if(isset($msg->error)){
                $this->showError($msg->error, $msg->error_description);
            }
        }
        
        $params = array();
        parse_str($response, $params);
        return $params["access_token"];
    }

    public function qq_openid($token){
        
        if(!$token)
            return false;
        
        //-------请求参数列表
        $keysArr = array(
            "access_token" => $token
        );

        $graph_url = $this->combineURL(self::GET_OPENID_URL, $keysArr);
        $response = $this->get_contents($graph_url);

        //--------检测错误是否发生
        if(strpos($response, "callback") !== false){
            $lpos = strpos($response, "(");
            $rpos = strrpos($response, ")");
            $response = substr($response, $lpos + 1, $rpos - $lpos -1);
        }

        $user = json_decode($response);
        
        if(isset($user->error)){
            $this->showError($user->error, $user->error_description);
        }
        return $user->openid;
    }
    
    public function qq_static_var($key, $val=null){
        static $i =0;
        
        if($i === 0 && !$_SESSION) // 暂时用session.
            session_start();
        
        // 只有一个参数时是读, 否则是写.
        if($val !== null){
           $_SESSION[$key] = $val; 
        }
        
        //无论如何都会返回!
        return $_SESSION[$key];
    }
    
    private function showError($code, $description = '$'){
        $this->errorMsg = array(
            '20001'=>'请将配置完全检查一遍',
            '30001' => 'state防止CSRF攻击, 验证未通过',
            '50001' => '请尝试开启curl支持,重启web服务器',
            );
        
        if(!$this->apperrreport){
            die();//die quietly
        }
    
        echo "<meta charset=\"UTF-8\">";
        if($description == "$"){
            die('<h2>'.$this->errorMsg[$code].'</h2>');
        }else{
            echo "<h3>error:</h3>$code";
            echo "<h3>msg  :</h3>$description";
        }
        exit(); 
    }
    
    private function combineURL($baseURL,$keysArr){
        $combined = $baseURL.'?';
        $valueArr = array();

        foreach($keysArr as $key => $val){
            if($val)
            $valueArr[] = "$key=$val";
        }

        $keyStr = implode("&",$valueArr);
        $combined .= ($keyStr);
        
        return $combined;
    }
    private function get_contents($url){
        if (!function_exists('curl_init')) {
            $response = file_get_contents($url);
        }else{
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
            curl_setopt($ch, CURLOPT_URL, $url);
            $response =  curl_exec($ch);
            curl_close($ch);
        }

        //-------请求为空
        if(empty($response)){
            $this->showError("50001");
        }

        return $response;
    }
}



<?php
    define('QQ_APPID','');
    define('QQ_APPKEY','');
    define('QQ_CALLBACK','http://www.os688.com/test.php?call=1'); //返回相同的url, 可以用参数.
    define('QQ_ERRORREPORT',true); // 是否调试.
    define('QQ_SCOPE',null); // 授权哪些项, 通常默认即可.
    
    include 'Oauth.class.php';
    $t = new Oauth();
    
    echo '<pre>';
    if($_GET['call']){
        $token = $t->qq_callback();
        echo 'token:'. $token."\n";
        $openid = $t->qq_openid($token);
        echo 'openid:'.$openid;
    }else{
        $t->qq_login();
    }
?>


调用示例, 一个类库即可解决QQ登录的问题, 配置也更加明了.

END





© 著作权归作者所有

共有 人打赏支持
Tuesday

Tuesday

粉丝 153
博文 64
码字总数 29329
作品 3
产品经理
加载中

评论(6)

Tuesday
Tuesday

引用来自“Unosay”的评论

Warning: session_start() [function.session-start]: Cannot send session cache limiter - headers already sent (output started at /data1/www/htdocs/3/shixiong/5/ceshi/in.php:1) in ceshi/Oauth.class.php on line 129



Warning: Cannot modify header information - headers already sent by (output started at /data1/www/htdocs/3/shixiong/5/ceshi/in.php:1) in ceshi/Oauth.class.php on line 55

一样不能用
把示例中的echo 最后一行....
Unosay
Unosay
Warning: session_start() [function.session-start]: Cannot send session cache limiter - headers already sent (output started at /data1/www/htdocs/3/shixiong/5/ceshi/in.php:1) in ceshi/Oauth.class.php on line 129



Warning: Cannot modify header information - headers already sent by (output started at /data1/www/htdocs/3/shixiong/5/ceshi/in.php:1) in ceshi/Oauth.class.php on line 55

一样不能用
Tuesday
Tuesday

引用来自“冷海圣”的评论

更加简明啦

token值是否保存, 主要看需求, 假如你做多应用, 就保存, 假如只是QQ登录, 并且用户信息跟QQ无关, 就可以不保存.
冷三叔
冷三叔
更加简明啦
chenlei65368
chenlei65368

引用来自“Rain2Zoey”的评论

看起来好利害!

虽然不知道说什么,看起来好厉害的样子
RUGBEN
RUGBEN
看起来好利害!
php实现QQ、微博第三方登录演示与下载

请直接登陆官方网站查看演示:http://www.erdangjiade.com/js/288.html 效果图片如下: 目前可用登录平台为:腾讯QQ,腾讯微博,新浪微博,网易微博,人人网,360,豆瓣,Github,Google,M...

2当家的 ⋅ 2017/08/15 ⋅ 0

suwei76/connect-python-sdk-oauth2-for-qq

connect-python-sdk-oauth2-for-qq = 简介 = PYTHON SDK基于QQ互联OAuth2.0协议的server-side模式, 封装了登录流程及调用类似获取用户信息所需的基础代码。 = 说明 = 开发者需按下面的说明修...

suwei76 ⋅ 2015/02/10 ⋅ 0

自定义网页QQ登录按钮

QQ互联为网页的 QQ 登录提供了统一接口,但是其样式比较固定,风格陈旧,没有提供完全自定义的接口。在此介绍一种自定义按钮风格的方法。 准备: 在 QQ 互联 申请网站介入 详见:http://wiki...

tonglei0429 ⋅ 2015/12/23 ⋅ 0

iOS 基于第三方QQ授权登录

iOS 基于第三方QQ授权登录 基于iOS实现APP的第三方QQ登陆,接入第三方SDK时的一个基本的步骤: 1,找到相关的开放平台,QQ互联平台,http://connect.qq.com/; 2,注册成功后创建自己的APP,...

Youth_关旋 ⋅ 2016/08/27 ⋅ 0

在自己的网站上实现QQ授权登录

最近在实现QQ授权登录,现将我的实现过程以及我的理解整理如下。以下所述如有不对之处,请指正。 官方提供的SDK有:JS,PHP,Java。我的网站使用Scala+Play搭建的,所以只能用JS SDk。 1.关键...

心尖偏左 ⋅ 2016/06/28 ⋅ 0

QQ互联-申请-连接

1.首先你要在qq官网上申请QQ互联,注意填写信息的时候要按照你备案的信息填写否则不通过。如图: 2.申请之后会给你一个appid和secert APP ID:10143*9 APP Key:b**c 3.下载sdk以php sdk为例...

gjanuary ⋅ 2017/12/23 ⋅ 0

QQ互联登录PHP-SDK,(-1)invalid openid错误—解决方法

QQ互联登录PHP-SDK,(-1)invalid openid错误—解决方法,问题现象是:部署了PHP-SDK之后,出现有时能登录,有时不能登录的情况,错误提示是:-1 client request's parameters are invalid, i...

李世晨 ⋅ 2013/12/31 ⋅ 0

connect-python-sdk-oauth2-for-qq

= 简介 = PYTHON SDK基于QQ互联OAuth2.0协议的server-side模式, 封装了登录流程及调用类似获取用户信息所需的基础代码。 = 说明 = 开发者需按下面的说明修改代码,并按需要调用skd中提供的方...

红薯 ⋅ 2013/12/26 ⋅ 0

QQ一键登录实现

首先,登录QQ互联平台http://connect.qq.com/manage/获取QQ一键登录所需的Appkey和Appsecret。 2、下载QQ登录用的SDK软件包,可以上QQ互联官方网下载。这里用的是ConnectPHPSDKforOAuth2_V1...

薇薇乐 ⋅ 2014/02/17 ⋅ 0

PhoneGap 集合 QQ互联

刚了解PhoneGap还没有深入,想先问问PhoneGap做应用,嵌入QQ互联(http://wiki.connect.qq.com/sdk下载),是不是使用 QQ互联的 JS SDK就可以呢。...

zhmsong ⋅ 2014/03/07 ⋅ 1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

IDEA PermGen space内存溢出

解决方案: File -> Settings -> Build, Execution, Deployment / Build Tools / Maven / Runner下,找到VM Options选项,默认是空的,改为如下内容(或更大值)...

快乐的小火柴 ⋅ 8分钟前 ⋅ 0

前端常见跨域解决方案

什么是跨域? 跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。 广义的跨域: 1.) 资源跳转: A链接、重定向、表单提交2.) 资源嵌入: <link>、<script>、<im...

临江仙卜算子 ⋅ 9分钟前 ⋅ 0

系统管理命令service

service命令用来控制系统服务的实用工具,例如启动、停止、重启和关闭系统服务,以及当前状态。当然也可以直接操作,例如/etc/init.d/mysqld restart等。 语法 service (选项)(参数) 选项...

Jpchina ⋅ 14分钟前 ⋅ 0

MySQL 联合索引的命中规则

为什么要用联合索引? 对于查询语句“SELECT T.* FROM T WHERE T.c1=1 AND T.c3=2”涉及到两列,这个时候我们一般采用一个联合索引(c1, c3);而不用两个单列索引,这是因为一条查询语句往往应...

hensemlee ⋅ 21分钟前 ⋅ 0

Spring 自动组件扫描

通常情况下都是在XML配置文件中手动声明Bean和组件的。不过Spring也可以自动扫描组件实例化Bean,这样就可以避免在XML文件中繁琐的Bean声明。 手动声明Bean: 这里不再啰嗦,就是简单地在XML...

霍淇滨 ⋅ 26分钟前 ⋅ 0

MapReduce简单需求分析-共同好友及查找互粉的情况

MapReduce的设计,最重要的是要找准key,然后制定一系列的数据处理流程。MapReduce的Map中,会把key相同的分配到同一个reduce中,对于key的选择,可以找到某个相同的因素。以下面的几个例子说...

Jason_typ ⋅ 28分钟前 ⋅ 0

springboot多数据源自动切换

SpringBoot多数据源切换,先上配置文件: 1.pom: <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20......

JackyRiver ⋅ 30分钟前 ⋅ 0

Boost库编译应用

版本:Boost 1.66.0 Windows库编译 官网指南:直接执行bootstrap.bat处理文件即可,可以我却遇到一堆的问题。 环境:Windows 10 + Visual Studio 2017 Boost编译出来库命名 boost库生成文件命...

水海云 ⋅ 34分钟前 ⋅ 0

解决Eclipse发布到Tomcat丢失依赖jar包的问题

如果jar文件是以外部依赖的形式导入的。Eclipse将web项目发布到Tomcat时,是不会自动发布这些依赖的。 可以通过Eclipse在项目上右击 - Propertics - Deployment Assembly,添加“Java Build ...

ArlenXu ⋅ 34分钟前 ⋅ 0

iview tree组件层级过多时可左右滚动

使用vue+iview的tree组件,iview官网iview的tree树形控件 问题描述:tree层级过多时左右不可滚动 问题解决:修改overflow属性值 .el-tree-node>.el-tree-node_children { overflow: vi...

YXMBetter ⋅ 36分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部