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 相关文章推荐
风格模板初级不完全修改教程
Oct 09 PHP
PHP+Mysql日期时间如何转换(UNIX时间戳和格式化日期)
Jul 15 PHP
php 调试利器debug_print_backtrace()
Jul 23 PHP
解析如何在PHP下载文件名中解决乱码的问题
Jun 20 PHP
解析thinkphp中的导入文件标签
Jun 20 PHP
PHP 实现代码复用的一个方法 traits新特性
Feb 22 PHP
简单谈谈favicon
Jun 10 PHP
PHP接收json 并将接收数据插入数据库的实现代码
Dec 01 PHP
php上传图片生成缩略图(GD库)
Jan 06 PHP
PHP简单实现DES加密解密的方法
Jul 12 PHP
PHP仿微信发红包领红包效果
Oct 30 PHP
php常用经典函数集锦【数组、字符串、栈、队列、排序等】
Aug 23 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 print类函数使用总结
2010/06/25 PHP
php 的加密函数 md5,crypt,base64_encode 等使用介绍
2012/04/09 PHP
使用php实现快钱支付功能(涉及到接口)
2013/07/01 PHP
php计算整个目录大小的方法
2015/06/01 PHP
PHP CURL采集百度搜寻结果图片不显示问题的解决方法
2017/02/03 PHP
php无限极分类实现方法分析
2019/07/04 PHP
谈谈关于JavaScript 中的 MVC 模式
2013/04/11 Javascript
node.js超时timeout详解
2014/11/26 Javascript
浅谈JavaScript中的Math.atan()方法的使用
2015/06/14 Javascript
全面解析Bootstrap排版使用方法(标题)
2015/11/30 Javascript
jQuery实现iframe父窗体和子窗体的相互调用
2016/06/17 Javascript
angular中的http拦截器Interceptors的实现
2017/02/21 Javascript
Vue项目中引入外部文件的方法(css、js、less)
2017/07/24 Javascript
layui点击按钮添加可编辑的一行方法
2018/08/15 Javascript
vue在线动态切换主题色方案
2020/03/26 Javascript
vue移动端下拉刷新和上滑加载
2020/10/27 Javascript
Python处理XML格式数据的方法详解
2017/03/21 Python
Python中查看文件名和文件路径
2017/03/31 Python
使用Python实现简单的服务器功能
2017/08/25 Python
python中利用await关键字如何等待Future对象完成详解
2017/09/07 Python
Sanic框架基于类的视图用法示例
2018/07/18 Python
python实现多层感知器
2019/01/18 Python
关于Python核心框架tornado的异步协程的2种方法详解
2019/08/28 Python
使用Python实现将多表分批次从数据库导出到Excel
2020/05/15 Python
微信小程序“圣诞帽”的实现思路详解
2017/12/28 HTML / CSS
旧时光糖果:Old Time Candy
2018/02/05 全球购物
以色列的身体护理及家居香薰品牌:Sabon NYC
2018/02/23 全球购物
描述RIP和OSPF区别以及特点
2015/01/17 面试题
中专毕业生求职简历的自我评价
2013/10/21 职场文书
迎八一活动主题
2014/01/31 职场文书
生物科学专业自荐书
2014/06/20 职场文书
2016年校园植树节广播稿
2015/12/17 职场文书
创业计划书之校园超市
2019/09/12 职场文书
浅谈PostgreSQL表分区的三种方式
2021/06/29 PostgreSQL
教你使用一行Python代码玩遍童年的小游戏
2021/08/23 Python
CSS+HTML 实现顶部导航栏功能
2021/08/30 HTML / CSS