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 相关文章推荐
解析posix与perl标准的正则表达式区别
Jun 17 PHP
PHP使用CURL实现对带有验证码的网站进行模拟登录的方法
Jul 23 PHP
php实现的发送带附件邮件类实例
Sep 22 PHP
php实现在限定区域里自动调整字体大小的类实例
Apr 02 PHP
php数组索引与键值操作技巧实例分析
Jun 24 PHP
php实现图片上传、剪切功能
May 07 PHP
php项目开发中用到的快速排序算法分析
Jun 25 PHP
Docker配置PHP开发环境教程
Dec 21 PHP
CI(CodeIgniter)框架实现图片上传的方法
Mar 24 PHP
POST一个JSON格式的数据给Restful服务实例详解
Apr 07 PHP
laravel接管Dingo-api和默认的错误处理方式
Oct 25 PHP
PHP7移除的扩展和SAPI
Mar 09 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
德生PL330的评价与改造
2021/03/02 无线电
php中使用array_filter()函数过滤空数组的实现代码
2014/08/19 PHP
thinkPHP实现MemCache分布式缓存功能
2016/03/23 PHP
PHP中的使用curl发送请求(GET请求和POST请求)
2017/02/08 PHP
ThinkPHP中create()方法自动验证表单信息
2017/04/28 PHP
yii2多图上传组件的使用教程
2018/05/10 PHP
javascript下过滤数组重复值的代码
2007/09/10 Javascript
Jquery数独游戏解析(一)-页面布局
2010/11/05 Javascript
js绑定事件this指向发生改变的问题解决方法
2013/04/23 Javascript
extjs表格文本启用选择复制功能具体实现
2013/10/11 Javascript
jquery模拟SELECT下拉框取值效果
2013/10/23 Javascript
深入理解JavaScript系列(30):设计模式之外观模式详解
2015/03/03 Javascript
JS实现的车标图片提示效果代码
2015/10/10 Javascript
javascript每日必学之循环
2016/02/19 Javascript
JavaScript必看小技巧(必看)
2016/06/07 Javascript
Javascript随机标签云代码实例
2016/06/21 Javascript
js实现select选择框效果及美化
2016/08/19 Javascript
Node连接mysql数据库方法介绍
2017/02/07 Javascript
Vue计算属性的使用
2017/08/04 Javascript
深入理解Vue2.x的虚拟DOM diff原理
2017/09/27 Javascript
详解vue-cli 构建项目 vue-cli请求后台接口 vue-cli使用axios、sass、swiper
2018/05/28 Javascript
使用原生JS实现火锅点餐小程序(面向对象思想)
2019/12/10 Javascript
JS绘图Flot如何实现动态可刷新曲线图
2020/10/16 Javascript
uniapp实现可滑动选项卡
2020/10/21 Javascript
Python编程中的异常处理教程
2015/08/21 Python
Python 爬虫之Beautiful Soup模块使用指南
2018/07/05 Python
Pyqt清空某一个QTreeewidgetItem下的所有分支方法
2019/06/17 Python
使用python获取(宜宾市地震信息)地震信息
2019/06/20 Python
python微信公众号开发简单流程实现
2020/03/09 Python
使用python图形模块turtle库绘制樱花、玫瑰、圣诞树代码实例
2020/03/16 Python
python + selenium 刷B站播放量的实例代码
2020/06/12 Python
英国在线自行车店:Merlin Cycles
2018/08/20 全球购物
学生自我评价范文
2014/02/02 职场文书
市场营销调查计划书
2014/05/02 职场文书
2014年团支部工作总结
2014/11/17 职场文书
2015年计生协会工作总结
2015/04/24 职场文书