PHP微信PC二维码登陆的实现思路


Posted in PHP onJuly 13, 2017

一、思路:

思路关键在于如何与微信端交互起来,毕竟目前微信登录只能是在微信端。

但是微信有一个特殊的方法用于生成自定义的二维码,这就让我们能够在PC上显示二维码,而二维码的值可以是我们定义的。另外看微信开发文档中存在一个scan事件,可以检测用户使用微信扫描二维码并获取值。其实问题的关键就在于这个值,这个值算是一个联通PC和微信的通信ID了。

二、具体实现流程(下面代码使用了TP5的框架,有个大前提是存在一个服务号的公众号)

1、生成PC端的二维码:

代码如下:

控制器:

namespace app\home\controller;

class Recognition extends Base{

  public function seeLoginQrcode(){
    $qrcode_return = model('Recognition')->getLoginQrcode();
    if($qrcode_return['error_code']){
      return $this->returnJson("获取失败!",0);
    }else{
      $data=array(
          'url'=>$qrcode_return['ticket'],
          'qrcode_id'=>$qrcode_return['id'],
      );
      return $this->returnJson("获取成功!",1,$data);
    }
  }
}

model:

namespace app\common\model;

use think\Model;
class Recognition extends Model{
  protected $autoWriteTimestamp = false;
  //生成登录用的临时二维码
  public function getLoginQrcode(){
    $appid   = config('THINK_SDK_WEIXIN.APP_KEY');
    $appsecret = config('THINK_SDK_WEIXIN.APP_SECRET');
    if(empty($appid) || empty($appsecret)){
      return(array('error_code'=>true,'msg'=>'请联系管理员配置【AppId】【 AppSecret】'));
    }

    $database_login_qrcode = model('LoginQrcode');
    $database_login_qrcode->where(array('add_time'=>array('lt',($_SERVER['REQUEST_TIME']-604800))))->delete();

    $data_login_qrcode['add_time'] = $_SERVER['REQUEST_TIME'];
    $database_login_qrcode->save($data_login_qrcode);
    $qrcode_id = $database_login_qrcode->getLastInsID();
    if(empty($qrcode_id)){
      return(array('error_code'=>true,'msg'=>'获取二维码错误!无法写入数据到数据库。请重试。'));
    }

    import('Net.Http');
    $http = new \Http();

    //微信授权获得access_token
    $access_token_array = model('AccessTokenExpires')->getAccessToken();
    if ($access_token_array['errcode']) {
      return(array('error_code'=>true,'msg'=>'获取access_token发生错误:错误代码' . $access_token_array['errcode'] .',微信返回错误信息:' . $access_token_array['errmsg']));
    }
    $access_token = $access_token_array['access_token'];

    $qrcode_url='https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token='.$access_token;
    $post_data['expire_seconds'] = 604800;
    $post_data['action_name'] = 'QR_SCENE';
    $post_data['action_info']['scene']['scene_id'] = $qrcode_id;

    $json = $http->curlPost($qrcode_url,json_encode($post_data));
    if (!$json['errcode']){
      $condition_login_qrcode['id']=$qrcode_id;
      $data_login_qrcode['id'] = $qrcode_id;
      $data_login_qrcode['ticket'] = $json['ticket'];
      if($database_login_qrcode->isUpdate(true)->save($data_login_qrcode)){
        return(array('error_code'=>false,'id'=>$qrcode_id,'ticket'=>'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket='.urlencode($json['ticket'])));
      }else{
        $database_login_qrcode->where($condition_login_qrcode)->delete();
        return(array('error_code'=>true,'msg'=>'获取二维码错误!保存二维码失败。请重试。'));
      }
    }else{
      $condition_login_qrcode['id'] = $qrcode_id;
      $database_login_qrcode->where($condition_login_qrcode)->delete();
      return(array('error_code'=>true,'msg'=>'发生错误:错误代码 '.$json['errcode'].',微信返回错误信息:'.$json['errmsg']));
    }
  }
}

可以看到成功后返回:

return(array('error_code'=>false,'id'=>$qrcode_id,'ticket'=>'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket='.urlencode($json['ticket'])));

其中有一个id值,其实代表的就是二维码的值!

然后ticket就是二维码的链接。也就是扫描这个二维码在scan事件获取的值就是这个id。

下面查看微信端处理

1、扫描二维码之后:

namespace app\mobile\controller;

class Wechat extends Base{

  public function index()
  {
    import('Wechat.Wechat');
    $wechat = new \Wechat();
    $data = $wechat->request();
    list($content, $type) = $this->reply($data);
    if ($content) {
      $wechat->response($content, $type);
    }
    else {
      exit();
    }
  }
  public function reply($data)
  {
    if ($data['MsgType'] == 'event') {
      $id = $data['EventKey'];
      switch (strtoupper($data['Event'])) {
        case 'SCAN':
          return $this->scan($id, $data['FromUserName']);
        case 'CLICK':
          //回复?
          return array('click', 'text');
          break;
        case 'SUBSCRIBE':
          //关注
          return array('Welcome', 'text');
          break;
        case 'UNSUBSCRIBE':
          //取关

          return array('BYE-BYE', 'text');
        case 'LOCATION':
          //定位

          break;
      }
    }
    else {
      if ($data['MsgType'] == 'text') {
        return array("测试成功!",'text');
      }

      if ($data['MsgType'] == 'location') {

      }

      if (import('@.ORG.' . $data['MsgType'] . 'MessageReply')) {

      }
    }

    return false;
  }
  private function scan($id, $openid = '', $issubscribe = 0)
  {
    if ((1000000000 < $id) && $openid) {
       if ($user = model('Member')->field('id')->where(array('third_id' => $openid))->find()) {
         $data=array(
           'id'=>$id,
           'uid'=> $user['id']
         );
         model('LoginQrcode')->isUpdate()->save($data);
         return array('登陆成功', 'text');
       }
       $data=array(
         'id'=>$id,
         'uid'=>-1
       );
       model('LoginQrcode')->isUpdate(true)->save($data);
      $return[] = array('点击授权登录', '',config('SITE_LOGO'), config('SITE_URL') . '/mobile/WechatBind/ajaxWebLogin?qrcode_id=' . $id);
      return array($return, 'news');
    }
  }
}

上面的Scan方法有这个判断,可以看到是:

if ((1000000000 < $id) && $openid) {

其中的$id,就是对应的二维码的值,也就是之前我们生成的那个id(其实我们为了区分Scan中的各种事件,特意将id所在的login_qrcode表自增id从1000000000开始)。
然后看if后面的处理:

if ($user = model('Member')->field('id')->where(array('third_id' => $openid))->find()) {
         $data=array(
           'id'=>$id,
           'uid'=> $user['id']
         );
         model('LoginQrcode')->isUpdate()->save($data);
         return array('登陆成功', 'text');
       }

如果满足条件,并且存在该openid的用户,则更新login_qrcode表,将uid改为用户id。(这里就是关键,为什么更新了id对应的那条数据的uid为用户id就算登录了呢)。

3、继续看PC端,PC段在获取1中的二维码之后并没有停止请求,而是轮训了一个方法:

* 微信登录异步请求
   * @return \think\response\Json
   * created by sunnier<xiaoyao_xiao@126.com>
   */
  public function ajaxWechatLogin(){
      for ($i = 0; $i < 6; $i++) {
        $database_login_qrcode = model('LoginQrcode');
        $condition_login_qrcode['id'] = input('get.qrcode_id');
        if(empty($condition_login_qrcode['id'])){
          return $this->returnJson('未获取到qrcode_id!',0);
        }
        $now_qrcode = $database_login_qrcode->field('`uid`')->where($condition_login_qrcode)->find();
        if (!empty($now_qrcode['uid'])) {
          if ($now_qrcode['uid'] == -1) {
            $data_login_qrcode['uid'] = 0;
            $database_login_qrcode->where($condition_login_qrcode)->isUpdate(true)->save($data_login_qrcode);
            return $this->returnJson('请在微信公众号点击授权登录!',0);
          }
          $database_login_qrcode->where($condition_login_qrcode)->delete();
          $result = model('Member')->autologin('id', $now_qrcode['uid']);
          if (empty($result['error_code'])) {
            return $this->returnJson('登录成功!',1,$result['user']);
          } else if ($result['error_code'] == 1001) {
            return $this->returnJson('没有查找到用户,请重新扫描二维码!',0);
          } else if ($result['error_code']) {
            return $this->returnJson('登陆失败!',0);
          }
        }
        if ($i == 5) {
          return $this->returnJson('登陆失败',0);
        }
        sleep(3);
      }
  }

可以看到上面方法获取了qrcode_id,也就是1中返回的那个id,另一个返回就是二维码了。
轮训过程就是用这个id不断查看login_qrcode表的状态,如果存在了uid那么证明登陆成功!也就可以用其中的uid自动登录了。

4、以上

关键就是login_qrcode这个中间表起了桥梁的作用,一边用来生成二维码,一边用来在微信端插入用户uid,同时PC端检测表的状态变化从而实现了登录。

三、代码仓库

https://git.oschina.net/kebenxiaoming/erwmlogin1

直接clone即可,有问题提issue或者单独私我

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
PHP 简易输出CSV表格文件的方法详解
Jun 20 PHP
php连接函数implode与分割explode的深入解析
Jun 26 PHP
php函数serialize()与unserialize()用法实例
Nov 06 PHP
Nginx下配置codeigniter框架方法
Apr 07 PHP
PHP中的类型约束介绍
May 11 PHP
PHP根据图片色界在不同位置加水印的方法
Jul 01 PHP
PHP易混淆知识整理笔记
Sep 24 PHP
php经典算法集锦
Nov 14 PHP
Zend Framework自定义Helper类相关注意事项总结
Mar 14 PHP
PHP模板引擎Smarty中的保留变量用法分析
Apr 11 PHP
[原创]解决wincache不支持64位PHP5.5/5.6的问题(提供64位wincache下载)
Jun 22 PHP
PHP中register_shutdown_function函数的基础介绍与用法详解
Nov 28 PHP
PHP基于socket实现客户端和服务端通讯功能
Jul 13 #PHP
php中请求url的五种方法总结
Jul 13 #PHP
Laravel 5.4重新登录实现跳转到登录前页面的原理和方法
Jul 13 #PHP
php获取'/'传参的值简单方法
Jul 13 #PHP
php多文件打包下载的实例代码
Jul 12 #PHP
php实现网页端验证码功能
Jul 11 #PHP
阿里云PHP SMS短信服务验证码发送方法
Jul 11 #PHP
You might like
php批量删除数据库下指定前缀的表以prefix_为例
2014/08/24 PHP
php类的扩展和继承用法实例
2015/06/20 PHP
php实现smarty模板无限极分类的方法
2015/12/07 PHP
解决Laravel5.x的php artisan migrate数据库迁移创建操作报错SQLSTATE[42000]
2020/04/06 PHP
javascript 二分法(数组array)
2010/04/24 Javascript
JavaScript运行机制之事件循环(Event Loop)详解
2014/10/10 Javascript
jQuery中复合属性选择器用法实例
2014/12/31 Javascript
使用jQuery实现返回顶部
2015/01/26 Javascript
javascript中new关键字详解
2015/12/14 Javascript
js实现移动端微信页面禁止字体放大
2017/02/16 Javascript
Vue.js如何优雅的进行form validation
2017/04/07 Javascript
JavaScript数据结构之二叉树的遍历算法示例
2017/04/13 Javascript
Vue项目中使用jquery的简单方法
2019/05/16 jQuery
解决layui调用自定义方法提示未定义的问题
2019/09/14 Javascript
[05:49]2014DOTA2TI4正赛第二日综述 昔日冠军纷纷落马 VG LGD占尽先机
2014/07/20 DOTA
Python3实现连接SQLite数据库的方法
2014/08/23 Python
讲解Python中fileno()方法的使用
2015/05/24 Python
python提取页面内url列表的方法
2015/05/25 Python
Python基本语法经典教程
2016/03/11 Python
Python实现的爬取网易动态评论操作示例
2018/06/06 Python
Python分析彩票记录并预测中奖号码过程详解
2019/07/09 Python
django foreignkey(外键)的实现
2019/07/29 Python
python 直接赋值和copy的区别详解
2019/08/07 Python
浅析使用Python搭建http服务器
2019/10/27 Python
opencv-python的RGB与BGR互转方式
2020/06/02 Python
python 用opencv实现图像修复和图像金字塔
2020/11/27 Python
台湾菁英交友:结识黄金单身的台湾人
2018/01/22 全球购物
澳洲女装时尚在线:Blue Bungalow
2018/05/05 全球购物
日本最大的彩色隐形眼镜销售网站:CharmColor
2020/09/09 全球购物
与UNIX有关的几个名词
2015/09/17 面试题
大学生职业生涯规划范文——找准自我,定位人生
2014/01/23 职场文书
国际经济与贸易专业大学生职业规划书
2014/03/01 职场文书
节能减排倡议书
2014/04/15 职场文书
乐观自信演讲稿范文
2014/05/21 职场文书
三八节活动主持词
2015/07/04 职场文书
还在手动盖楼抽奖?教你用Python实现自动评论盖楼抽奖(一)
2021/06/07 Python