首页 > 编程语言 >PHP关注公众号后网站自动注册并登录的实现

PHP关注公众号后网站自动注册并登录的实现

时间:2023-09-21 11:45:42浏览次数:70  
标签:function 登录 admin 微信 二维码 注册 PHP data

需求描述
在自己网站上点击微信登录,网站自己弹出一个二维码、扫描二维码后弹出公众号的关注界面、只要一关注公众号网站自动登录、第二次扫描登录的时候网站直接登录。
大家可以体验一下 「随便找的一个网站」

前期准备
一个公众号(必须认证,配置服务器)
微信开发文档

实现原理
公众平台提供了生成带参二维码的接口。使用该接口可以生成带不同场景值的二维码,用户扫描后,公众号可以接收到扫码/关注事件推送,在细分如下:

  • 扫描二维码,如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值(自定义值)关注事件推送给开发者。
  • 扫描二维码,如果用户已经关注公众号,在用户扫描后会自动进入会话,微信也会将带场景值(自定义值)扫码事件推送给开发者。

设计如下流程:

  1. 生成二维码的时候你自定义一个参数到二维码中,顺便把这个参数传到前端页面中。
  2. 用户扫码关注后微信服务器发送一个关注事件或扫码事件消息到自己服务端,消息参数中包括了自定义参数和扫码用户openid等参数。
  3. 根据openid用微信公众号接口去获取用户信息,拿到用户信息之后就是实现注册逻辑,用自定义参数标记作为缓存key标记可以登录。
  4. 前端轮询查询定义参数为key的缓存是否标记可登录时,就开始实现登录逻辑,重载页面,流程完毕。

实战
1.微信公众号部署服务器和Token认证
由于我们自己服务,需要接管微信服务器的推送,所以需要在微信公众号后台配置服务器通知地址,公众号测试号的配置大同小异这里不做累述,如图所示:

注:这个配置启用后,微信服务器会把相关的事件推送都转发到用户服务器上,同时微信后台的自定义菜单和回复也失效,需要用户在自己服务器上通过微信接口来接管

Token验证示例代码:

<?php
// 微信token认证
$signature = $_GET["signature"];
$timestamp = $_GET["timestamp"];
$nonce = $_GET["nonce"];
$echostr = $_GET["echostr"];
// 你在微信公众号后台的设置的Token
$token = "";
 
// 1)将token、timestamp、nonce三个参数进行字典序排序
$tmpArr = array($nonce,$token,$timestamp);
sort($tmpArr,SORT_STRING);
 
// 2)将三个参数字符串拼接成一个字符串进行sha1加密
$str = implode($tmpArr);
$sign = sha1($str);
 
// 3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
if ($sign == $signature) {
	echo $echostr;
}

2.服务端生成带参二维码
先了解下,公众号的带参二维码生成 接口文档 ,我们选择临时二维码,步骤如下:

第一步:通过AppID和AppSecret获取access_token
这里的access_token和上边的token不是一回事,不要混淆。获取access_token接口文档 ,这一步建议抽离封装,以便其他接口复用。

第二步:通过access_token创建二维码ticket
每次创建二维码ticket需要提供一个开发者自行设定的参数(scene_id),分别介绍临时二维码和永久二维码的创建二维码ticket过程。

第三步:通过ticket换取二维码
这一步其实就是拼接成一个二维码图片地址:https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET 提醒:TICKET记得进行UrlEncode,然后把这个二维码图片地址,返回给前端直接显示

示例代码:

    /**
     * 获取微信场景二维码
     * @param $sceneValue 场景值
     * @param int $type 1:临时二维码  2:永久二维码
     * @return string
     */
    public static function getWeChatUrl($sceneValue,$type = 1){
        $accessToken = WxToken::getToken();
        $url = 'https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token='.$accessToken;
        $actionName = 'QR_STR_SCENE';
        if($type==2){
            $actionName = 'QR_LIMIT_STR_SCENE';
        }
 
        $res = self::post($url,json_encode([
            'expire_seconds'=>604800,
            'action_name'=>$actionName,
            'action_info'=>[
                'scene'=>[
                    'scene_id'=>$sceneValue,
                    'scene_str'=>$sceneValue
                ]
            ]
        ]));
        if(isset($res['errcode']) && $res['errcode']){
            echo $res['errmsg'];die();
        }
        return 'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket='.$res['ticket'];
    }

3.前端请求二维码,轮询状态(也可使用websocket)

    // 方便清除轮询
    var timer = null;
    var flag = '';
 
    $(document).on('click', '.wechat-login', function () {
        // 请求登录二维码
        $.get('/admin/index/weChatQrCode', function (data, status) {
            $('.wechat-url').attr('src', data.data.qr_code_url);
 
            // 显示二维码
            flag = data.data.scene_value;
 
            // 轮询登录状态
            timer = setInterval(function () {
                // 对话框关闭时,清除轮询
                if ($('#wx-login-pan').attr('class').indexOf('open') == -1) {
                    clearInterval(timer);
                    return;
                }
                // 请求参数是二维码中的场景值
 
                $.get('/admin/index/loginCheck?wechat_flag='+flag, function (data, status) {
                    if(data.code==0){
                       window.location.href = '/home/serve/apply';
                    }
                });
            }, 2000);
        });
    });
 
    // 返回时清除轮询
    $('.wechat-back').click(function () {
        clearInterval(timer)
    });
 
    function closeLoginPan() {
        clearInterval(timer);
    }

4.后端扫码事件接受处理

// 微信事件接收类
class WxPush
{
    /**
     * 获得请求时POST:XML字符串
     * 不能用$_POST获取,因为没有key
     */
    public static function getResponse()
    {
        $xmlStr = $GLOBALS['HTTP_RAW_POST_DATA'];
        Log::record($xmlStr, 'wx_push');
        if (empty($xmlStr)) {
            return false;
        }
        // 解析该xml字符串,利用simpleXML
        libxml_disable_entity_loader(true);
        //禁止xml实体解析,防止xml注入
        $xml = simplexml_load_string($xmlStr, 'SimpleXMLElement', LIBXML_NOCDATA);
        $data = json_decode(json_encode($xml),true);
        //判断该消息的类型,通过元素MsgType
        Log::write($data, 'wx_push');
        switch ($data['MsgType']) {
            case 'event': // 事件处理
                self::handleEvent($data);
                break;
            case 'text'://文本消息
 
                break;
            case 'image'://图片消息
 
                break;
            case 'voice'://语音消息
 
                break;
            case 'video'://视频消息
 
                break;
            case 'shortvideo'://短视频消息
 
                break;
            case 'location'://位置消息
 
                break;
            case 'link'://链接消息
 
                break;
        }
    }
 
    /**
     * 事件引导处理方法(事件有许多,拆分处理)
     *
     * @param $data
     * @return mixed
     * @internal param $request
     * @internal param $event
     */
    static function handleEvent($data)
    {
        $method = strtolower($data['Event']);
        $event = new Event();
        if (method_exists($event, $method)) {
            return call_user_func_array([$event, $method], [$data]);
        }
    }
 
}
// 事件类
class Event
{
    /**
     * 扫描带参二维码事件
     */
    public function scan($data)
    {
        // 标记前端可登陆
        Admin::markLogin($data['EventKey'],$data['FromUserName']);
    }
 
    /**
     * 关注订阅
     */
    public function subscribe($data)
    {
        // 关注事件的场景值会带一个前缀需要去掉
        $data['EventKey'] = str_replace('qrscene_','',$data['EventKey']);
 
        // 标记前端可登陆
        Admin::markLogin($data['EventKey'],$data['FromUserName']);
    }
 
    /**
     * 取消订阅
     */
    public function unsubscribe($data)
    {
        $adminName = $data['FromUserName'];
        $admin = Admin::get(['wx_openid'=>$data['FromUserName']]);
        if($admin && $admin['name']){
            $adminName = $admin['name'];
        }
    }
 
    /**
     *  菜单点击事件
     */
    public function click(){
 
    }
 
    /**
     * 连接跳转事件
     */
    public function view(){
 
    }
}
    /**
     * 标记可登录
     */
    public static function markLogin($key,$val)
    {
        $admin = self::get(['wx_openid'=>$val]);
        $time = time();
        if(empty($admin)){
            $wxUser = WxUser::getCgiUserInfo($val);
            $admin = self::create([
                'wx_openid'=>$val,
                'name'=>Emoji::encode($wxUser['nickname']),
                'head_img_url'=>$wxUser['headimgurl'],
                'sex'=>$wxUser['sex'],
                'province'=>$wxUser['province'],
                'city'=>$wxUser['city'],
                'type'=>3,// 客户
                'create_time'=>$time,
                'update_time'=>$time
            ]);
        }
        // 写入缓存
        Cache::set($key, $val,60*60);
    }

5.登录检查
在前端轮询请求检查登录状态接口时,把自定义参数传给后端,后端以该自定义参数为key去缓存查询可登录状态,查到可以登录了则去授权登录,返回登录成功。

    /**
     * 微信用户登录检查
     *
     */
    public function loginCheck()
    {
        // 判断请求是否有微信登录标识
        if (!$flag = $this->request->get('wechat_flag')) {
            $this->apiError(-1,'标识不能为空');
        }
 
        // 根据微信标识在缓存中获取需要登录用户的 UID
        $uid  = Cache::get($flag);
        if(empty($uid)){
            $this->apiError(-1,'void');
        }
        $admin = Admin::get(['wx_openid'=>$uid]);
        if(empty($admin)){
            $this->apiError(-1,'用户未注册');
        }
        Admin::doLogin($admin);
        $this->apiSuccess($flag);
 
    }

 

标签:function,登录,admin,微信,二维码,注册,PHP,data
From: https://www.cnblogs.com/guangzhiruijie/p/17719549.html

相关文章

  • PHP微信扫码登录
    微信扫码登录总体说明:先获取token和ticket,通过微信生成二维码接口生成二维码,把二维码信息添加到数据表中,用户扫码时检测二维码扫描状态,扫描成功后更新二维码状态,跳转页面。 微信开放文档一、数据表qrcord表,用户存储二维码信息,每生成一个二维码生成一条记录,通过openid字段判断......
  • c++ 读写注册表
    classCConfig{HKEY_hKey;public:~CConfig(){if(_hKey){RegCloseKey(_hKey);}}CConfig():_hKey(0){}LSTATUSSave(PCWSTRlpValueName,DWORDdwType,co......
  • selenium自动化测试-登录网站用户
    昨天学习了selenium自动化测试工具的入门,知道了Selenium是用于自动化控制浏览器做各种操作,打开网页,点击按钮,输入表单等等。今天学习通过selenium自动化测试工具自动登录某网站用户操作。第一步:确定目标网址比如:天天基金网站登录页面"https://login.1234567.com.cn/login"第二......
  • php通过curl获取数据
    <?phpheader("Content-Type:text/html;charset=UTF-8");$url='https://www.baidu.com';print_r(curlContent($url));functioncurlContent($url,$method='get',$dataArr=array(),$headerArr=array()){$method=strtolower($......
  • PHP多层级菜单树形结构递归处理
    如题:一、数据库菜单数据表使用图片中id和parent_id两个参数来关联父子关系二、将数据库中的数据变成树状多层级解构```{ "id":1, "parentId":0, "treePath":"0", "name":"系统管理", "type":2, "path":"/system",......
  • 【漏洞复现】深信服 SG上网优化管理系统 catjs.php 任意文件读取漏洞
    1、简介2、漏洞描述深信服SG上网优化管理系统catjs.php存在任意文件读取漏洞,攻击者通过漏洞可以获取服务器上的敏感文件3、受影响版本深信服SG上网优化管理系统4、FOFA语句title==“SANGFOR上网优化管理”5、漏洞复现POCPOST/php/catjs.phpHTTP/1.1Host:User-A......
  • 注册npm账号,并完成首次验证
    在npm官网完成注册https://www.npmjs.com/打开命令行执行命令 i.查看npm当前源(npmconfiggetregistry或者npmconfiglist)ii.设置回原本的源,用来发布npm包(npmconfigsetregistryhttps://registry.npmjs.org/)iii.设置为淘宝镜像(npmconfigsetregistryhttps://......
  • 用户注册与登录界面java源码(带验证码)
    importjavax.swing.*;importjava.awt.*;importjava.util.Random;publicclassRegistrationSystemextendsJFrame{privatefinalJTextFieldusernameTextField;privatefinalJPasswordFieldpasswordField;......
  • nacos1.4.X版本服务注册源码分析
     客户端:nacos1.4.1版本服务注册流程1:依赖spring-cloud-starter-alibaba-nacos-discovery2:resources/META-INF/spring.factories自动配置NacosServiceRegistryAutoConfiguration3:自动配置类NacosAutoServiceRegistration继承AbstractAutoServiceRegistration实现Appli......
  • php实现大文件断点续传下载实例
    php实现大文件断点续传下载实例,看完你就知道超过100M以上的大文件如何断点传输了,这个功能还是比较经典实用的,毕竟大文件上传功能经常用得到。1require_once('download.class.php');2date_default_timezone_set('Asia/Shanghai');3error_reporting(E_STRICT);4......