PHP微信公众号扫码登录 thinkphp

php、thinkphp 微信公众号扫码登录、绑定

文章目录

  • 环境
  • 绑定
  • 二、登录
  • 总结


环境

Linux 、Nginx 、PHP 、Mysql、Thinkphp

前提

1、给你的用户表/管理员表 增加一个字段 open_id 字符串类型 长度例如 50 ;

2、建一个微信登录扫码表

domain_id 是我的业务代码ID 可以去除

CREATE TABLE `tp_admin_wechat_login_log` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `type` tinyint(1) NOT NULL DEFAULT '1' COMMENT '类型(1登录2绑定)',
  `ticket` varchar(128) NOT NULL DEFAULT '' COMMENT '二维码票据',
  `admin_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '管理员ID',
  `domain_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '场所ID',
  `open_id` varchar(50) NOT NULL DEFAULT '' COMMENT 'open_id',
  `is_login` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '是否登录/绑定(0否1是)',
  `create_time` bigint(16) NOT NULL DEFAULT '0' COMMENT '创建时间',
  `update_time` bigint(16) unsigned DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台微信登录表';

开始

一、绑定

1.微信操作类

代码如下:

我去除掉了一些无关的代码 然后获取accessToken那里用了缓存 注意替换或删除  然后需要配置APPID和SECRET

<?php

namespace app\common\library;

use think\Cache;
use think\Controller;
use think\Db;
use think\Log;

/**
 * 微信操作类
 */
class WxFunction extends Controller
{

    protected $APPID = '';
    protected $APPSECRET = '';
    protected $TOKENCACHENAME = '';

    public function _initialize()
    {
        $this->APPID = config('wxpay.APPID');
        $this->APPSECRET = config('wxpay.APPSECRET');
    }



    public function curl($url)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        $res = curl_exec($ch);
        curl_close($ch);
        return $res;
    }


    /**
     * @return string
     *得到普通的accesstoken
     */
    public function getGeneralAccessToken()
    {

         $this->APPID = config('wxpay.APPID');

         $this->APPSECRET = config('wxpay.APPSECRET');

         $this->TOKENCACHENAME = 'accessToken';

        return Cache::remember($this->TOKENCACHENAME, function () {

            try {
                $get_token_url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' . $this->APPID . '&secret=' . $this->APPSECRET;

                $data = $this->curl($get_token_url);

                $json_obj = json_decode($data, true);

                if ($this->TOKENCACHENAME == 'accessToken') {
                    return $json_obj["access_token"];
                } else {
                    return [
                        'accessToken' => $json_obj["access_token"],
                        'expirationTime' => time() + 3600,
                    ];

                }

            } catch (\Exception $e) {
                // 记录报错 或 抛出异常
            }

        }, 3600);
    }

    /**
     * @param $url
     * @param $json
     * @return mixed
     * 使用curl方法发送json数据
     */
    public function postData($url, $json)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        $res = curl_exec($ch);
        curl_close($ch);
        return $res;
    }

    /**
     * @param $access_token
     * @param $action_name
     * @param $scene_id
     * @return string 微信返回的全部信息
     */
    public function getQrcodeUrl($scene_id, $action_name = 'QR_LIMIT_STR_SCENE')
    {

        $access_token = $this->getGeneralAccessToken();

        $data = [
            "action_name" => $action_name,
            "action_info" => [
                "scene" => [
                    "scene_str" => $scene_id,
                ],
            ],
        ];
        $url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" . $access_token;

        $res = $this->postData($url, json_encode($data));

        return $res;
    }


}

2.微信回调函数

代码如下:

关注和未关注走的是两个方法 需注意的是 微信访问的时候 需要让他能访问到 例如:不需要登录

<?php

namespace app\api\controller;

use app\admin\model\Admin;

class Wxserver
{

    public function index()
    {

        $xml = file_get_contents('php://input', 'r');

        //转成php数组 禁止引用外部xml实体

        libxml_disable_entity_loader(true);

        $data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);

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

            switch ($data['Event']) {

                case 'subscribe':

                    return $this->subscribe($data);

                    break;

                case 'SCAN':

                    return $this->scan($data);

                    break;

                default:

                    # code...

                    break;

            }

        } else {

            die(' ');
        }
    }

    //未关注用户
    protected function subscribe($data)
    {

        $data['EventKey'] = str_replace('qrscene_', '', $data['EventKey']);

        //如果不带参数关注

        if ($data['EventKey'] == []) {

            

        } //如果带参数关注

        else {

            $EventKey = json_decode($data['EventKey'], true);

            switch ($EventKey['type']) {
                case 2:
                    Admin::bind_wechat_start($EventKey['admin_id'], $data['FromUserName'], $EventKey['domain_id'], $data['Ticket']);
                    break;
                case 3:
                    Admin::wechat_login($data['FromUserName'], $EventKey['domain_id'], $data['Ticket']);
                    break;
                default:
                    // 其他业务代码
                    break;

            }
        }

    }

    //已关注用户
    protected function scan($data)
    {
        // 合并配置数据和订单数据

        $EventKey = json_decode($data['EventKey'], true);

        switch ($EventKey['type']) {
            case 1:

                // 其他业务代码

                break;

            case 2:

                Admin::bind_wechat_start($EventKey['admin_id'], $data['FromUserName'], $EventKey['domain_id'], $data['Ticket']);

                break;

            case 3:

                Admin::wechat_login($data['FromUserName'], $EventKey['domain_id'], $data['Ticket']);

                break;

            default:

                # code...

                break;
        }
    }


}

3.开始绑定

绑定、解绑、检测绑定 。 把业务代码替换成自己的或者删除。  绑定是在登录状态下才可以绑定的。  我们把scene_id 弄成json的格式 更方便我们弄多个自定义数据

public function bind_wechat()
    {
        $url = '/';

        $admin_id = $this->auth->id;
        $domain = $this->request->get('domain');

        if (empty($admin_id) || empty($domain)) {
            $this->error("参数错误", $url);
        }

        $open_id = Db::name('admin')
            ->where('id', $admin_id)
            ->where('status', 'normal')
            ->value('open_id');

        if (!empty($open_id)) {
            $this->error("已绑定过微信");
        }

        $domain_id = \app\admin\model\Domain::where('domain',$domain)
            ->where('status',1)
            ->value('id');

        if(!$domain_id) {
            $this->error("场所不存在");
        }

        $param = ['type' => 2, 'admin_id' => $admin_id, 'domain_id' => $domain_id];
        $scene_id = json_encode($param);

        $wx = new WxFunction();
        $qrcodeRes = json_decode($wx->getQrcodeUrl($scene_id, 'QR_STR_SCENE'), true);

        if (empty($qrcodeRes['errcode'])) {
            $ticket = $qrcodeRes['ticket'];
            // 通过ticket换取二维码
            $qrcode = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" . $ticket;
            $data['qrcode'] = $qrcode;
            $data['scene_id'] = $scene_id;

            Db::table('tp_admin_wechat_login_log')
                ->insert([
                    'type' => 2,
                    'ticket' => $ticket,
                    'admin_id' => $admin_id,
                    'domain_id' => $domain_id,
                    'is_login' => 0,
                    'create_time' => time()
                ]);

        } else {
            Log::error($qrcodeRes['errcode'] . "错误信息" . $qrcodeRes['errmsg']);
            $this->error("获取二维码失败", $url);
        }

        $background = Config::get('fastadmin.login_background');
        $background = stripos($background, 'http') === 0 ? $background : config('site.cdnurl') . $background;
        $this->view->assign('background', $background);
        $this->view->assign('title', "绑定微信");

        $this->view->assign('data', $data);
        return $this->view->fetch();
    }


    public function checkBind()
    {
        $url = '/';

        $scene_id = $this->request->post('scene_id');
        if (empty($scene_id)) {
            $this->error("参数错误", $url);
        }

        $scene_id = json_decode($scene_id, true);

        $admin_id = $scene_id['admin_id'];
        $domain_id = $scene_id['domain_id'];

        if (empty($admin_id) || empty($domain_id)) {
            $this->error("参数错误", $url);
        }

        $open_id = Db::name('admin')
            ->where('id', $admin_id)
            ->where('domain_id', $domain_id)
            ->where('status', 'normal')
            ->value('open_id');

        if (empty($open_id)) {
            $this->error("暂未绑定");
        }

        $this->success("绑定成功");
    }

    public function unbind_wechat()
    {
        $url = '/';

        $admin_id = $this->auth->id;

        if (empty($admin_id)) {
            $this->error("参数错误", $url);
        }

        Db::name('admin')
            ->where('id', $admin_id)
            ->where('status', 'normal')
            ->update(['open_id' => '']);

        $this->success("解绑成功");
    }

绑定微信页面模板   重点是   js轮询

<!DOCTYPE html>
<html lang="en">
<head>
    {include file="common/meta" /}
    <style type="text/css">
        body {
            color: #999;
            background: url('{$background}');
            background-size: cover;
        }

        a {
            color: #fff;
        }

        .login-panel {
            margin-top: 150px;
        }

        .login-screen {
            max-width: 400px;
            padding: 0;
            margin: 100px auto 0 auto;

        }

        .login-screen .well {
            border-radius: 3px;
            -webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            background: rgba(255, 255, 255, 0.2);
        }

        .login-screen .copyright {
            text-align: center;
        }

        @media (max-width: 767px) {
            .login-screen {
                padding: 0 20px;
            }
        }

        .profile-img-card {
            width: 100px;
            height: 100px;
            margin: 10px auto;
            display: block;
            -moz-border-radius: 50%;
            -webkit-border-radius: 50%;
            border-radius: 50%;
        }

        .profile-name-card {
            text-align: center;
        }

        #login-form {
            margin-top: 20px;
        }

        #login-form .input-group {
            margin: auto auto 15px;
        }

    </style>
</head>
<body>
<div class="container">
    <div class="login-wrapper">
        <div class="login-screen">
            <div class="well">
                <div class="login-form">
                    <img id="profile-img" class="profile-img-card" src="__CDN__/assets/img/avatar.png"/>
                    <p id="profile-name" class="profile-name-card"></p>

                    <form action="" method="post" id="login-form">
                        <div id="errtips" class="hide"></div>

                        <div class="input-group">
                            <img src="{$data.qrcode}" style="width: 250px;box-shadow:0 0 10px #F1F3F4">
                            <input type="hidden" name="scene_id" id="scene_id" value='{$data.scene_id}'/>
                        </div>

                    </form>
                </div>
            </div>


            <p class="copyright"><a href="http://www.beian.miit.gov.cn">赣ICP备17005368号-1</a></p>


        </div>
    </div>
</div>


{include file="common/script" /}
<script src="/assets/js/jquery.min.js"></script>

<script>
    let c;

    // 轮询用户是否已经扫码
    $(document).ready(function () {
        c = setInterval(check_login, 2000);   //每2秒执行一次
    });

    //检测用户是否已扫码
    function check_login() {
        const scene_id = $("input[name='scene_id']").val();
        $.ajax({
            url: '/index/checkBind',
            data: {scene_id: scene_id},
            type: 'POST',
            dataType: 'JSON',
            success: function (res) {
                if (res.code === 1) {
                    // 扫码成功
                    alert(res.msg);
                    window.clearInterval(c); //终止轮询
                    window.location.href = '/index/index';
                }
            }
        })
    }
</script>

</body>
</html>

 回调走的方法

<?php

namespace app\admin\model;

use think\Db;
use think\Exception;
use think\Model;

class Admin extends Model
{

    // 开启自动写入时间戳字段
    protected $autoWriteTimestamp = 'int';
    // 定义时间戳字段名
    protected $createTime = 'createtime';
    protected $updateTime = 'updatetime';


    public static function bind_wechat_start($admin_id, $open_id, $domain_id, $ticket)
    {
        if (!isset($admin_id) || !isset($open_id) || !isset($domain_id) || !isset($ticket)) {
            throw new Exception("参数错误");
        }

        $self = new self();

        $self->where('id', $admin_id)
            ->where('domain_id', $domain_id)
            ->where('status', 'normal')
            ->update(['open_id' => $open_id]);

        Db::table('tp_admin_wechat_login_log')
            ->where('ticket', $ticket)
            ->where('admin_id', $admin_id)
            ->where('domain_id', $domain_id)
            ->where('is_login', 0)
            ->update(['open_id' => $open_id, 'is_login' => 1, 'update_time' => time()]);

        return true;
    }

    public static function wechat_login($open_id, $domain_id, $ticket)
    {
        if (empty($open_id) || empty($domain_id) || empty($ticket)) {
            throw new Exception("参数错误");
        }

        $admin = Admin::get(['open_id' => $open_id, 'domain_id' => $domain_id, 'status' => 'normal']);

        if (!$admin) {
            throw new Exception("用户不存在");
        }

        Db::table('tp_admin_wechat_login_log')
            ->where('ticket', $ticket)
            ->where('domain_id', $domain_id)
            ->where('is_login', 0)
            ->update(['admin_id' => $admin->id,'open_id' => $open_id, 'is_login' => 1, 'update_time' => time()]);

        return true;
    }

}

二、登录

登录的逻辑和绑定就差不多了 扫码后把自己的登录的逻辑就可以了

1、开始登录

public function wechat_login()
    {
        $domain = $this->request->get('domain');

        $url = '/';

        if ($this->auth->isLogin()) {
            $this->success(__("You've logged in, do not login again"), 'index/index');
        }

        if (empty($domain)) {
            $this->error("参数错误", $url);
        }

        $domain_id = \app\admin\model\Domain::where('domain',$domain)
            ->where('status',1)
            ->value('id');

        if(!$domain_id) {
            $this->error("场所不存在");
        }

        $param = ['type' => 3, 'domain_id' => $domain_id];

        $scene_id = json_encode($param);

        $wx = new WxFunction();
        $qrcodeRes = json_decode($wx->getQrcodeUrl($scene_id, 'QR_STR_SCENE'), true);

        if (empty($qrcodeRes['errcode'])) {
            $ticket = $qrcodeRes['ticket'];
            // 通过ticket换取二维码
            $qrcode = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" . $ticket;
            $data['qrcode'] = $qrcode;
            $data['scene_id'] = $scene_id;
            $data['ticket'] = $ticket;

            Db::table('tp_admin_wechat_login_log')
                ->insert([
                    'type' => 2,
                    'ticket' => $ticket,
                    'admin_id' => 0,
                    'domain_id' => $domain_id,
                    'is_login' => 0,
                    'create_time' => time()
                ]);
        } else {
            Log::error($qrcodeRes['errcode'] . "错误信息" . $qrcodeRes['errmsg']);
            $this->error("获取二维码失败", $url);
        }

        $background = Config::get('fastadmin.login_background');
        $background = stripos($background, 'http') === 0 ? $background : config('site.cdnurl') . $background;
        $this->view->assign('background', $background);
        $this->view->assign('title', "微信登录");

        $this->view->assign('data', $data);
        return $this->view->fetch();
    }

    public function checkLogin()
    {
        $url = '/';

        $scene_id = $this->request->post('scene_id');
        $ticket = $this->request->post('ticket');

        if (empty($scene_id) || empty($ticket)) {
            $this->error("参数错误", $url);
        }

        $scene_id = json_decode($scene_id, true);

        $domain_id = $scene_id['domain_id'];

        if (empty($domain_id)) {
            $this->error("参数错误", $url);
        }

        $is_login = Db::table('tp_admin_wechat_login_log')
            ->where('ticket', $ticket)
            ->where('domain_id', $domain_id)
            ->where('is_login', 1)
            ->find();

        if (empty($is_login)) {
            $this->error("暂未扫码登录");
        }

        $domain_field = [
            'md.id' => 'id',
            'md.name' => 'name',
            'md.domain' => 'domain',
            'md.MCHID' => 'MCHID',
            'da.proportion' => 'proportion',
        ];

        $domain = Db::table('main_domain')
            ->alias('md')
            ->field($domain_field)
            ->join(['dls_admin' => 'da'], 'md.pid=da.id', 'left')
            ->where('md.id', $domain_id)
            ->where('md.status', '1')
            ->find();

        if (!$domain) {
            $this->error("场所不存在");
        }

        if (is_null($domain['proportion']) or !isset($domain['proportion'])) {
            $domain['proportion'] = 0;
        }

        if (is_null($domain)) {
            return false;
        }

        $admin_id = $is_login['admin_id'];

        $admin = Admin::get(['id' => $admin_id, 'domain_id' => $domain['id'], 'open_id' => $is_login['open_id']]);

        if(!$admin) {
            $this->error("用户不存在");
        }

        $admin->loginfailure = 0;
        $admin->logintime = time();
        $admin->token = Random::uuid();
        $admin->save();
        $admin->domain = [
            'domain_id' => $domain['id'],
            'domain_name' => $domain['name'],
            'proportion' => $domain['proportion'],
        ];
        $admin->wechat = [
            'MCHID' => $domain['MCHID'],
        ];

        Session::set("admin", $admin->toArray());

        $keeptime = 86400;

        $expiretime = time() + $keeptime;
        $key = md5(md5($admin_id) . md5($keeptime) . md5($expiretime) . $admin->token);
        $data = [$admin_id, $keeptime, $expiretime, $key];
        Cookie::set('keeplogin', implode('|', $data), 86400 * 30);

        $this->success("登录成功");
    }
<!DOCTYPE html>
<html lang="en">
<head>
    {include file="common/meta" /}
    <style type="text/css">
        body {
            color: #999;
            background: url('{$background}');
            background-size: cover;
        }

        a {
            color: #fff;
        }

        .login-panel {
            margin-top: 150px;
        }

        .login-screen {
            max-width: 400px;
            padding: 0;
            margin: 100px auto 0 auto;

        }

        .login-screen .well {
            border-radius: 3px;
            -webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            background: rgba(255, 255, 255, 0.2);
        }

        .login-screen .copyright {
            text-align: center;
        }

        @media (max-width: 767px) {
            .login-screen {
                padding: 0 20px;
            }
        }

        .profile-img-card {
            width: 100px;
            height: 100px;
            margin: 10px auto;
            display: block;
            -moz-border-radius: 50%;
            -webkit-border-radius: 50%;
            border-radius: 50%;
        }

        .profile-name-card {
            text-align: center;
        }

        #login-form {
            margin-top: 20px;
        }

        #login-form .input-group {
            margin: auto auto 15px;
        }

    </style>
</head>
<body>
<div class="container">
    <div class="login-wrapper">
        <div class="login-screen">
            <div class="well">
                <div class="login-form">
                    <img id="profile-img" class="profile-img-card" src="__CDN__/assets/img/avatar.png"/>
                    <p id="profile-name" class="profile-name-card"></p>

                    <form action="" method="post" id="login-form">
                        <div id="errtips" class="hide"></div>

                        <div class="input-group">
                            <img src="{$data.qrcode}" style="width: 250px;box-shadow:0 0 10px #F1F3F4">
                            <input type="hidden" name="scene_id" id="scene_id" value='{$data.scene_id}'/>
                            <input type="hidden" name="ticket" id="ticket" value='{$data.ticket}'/>
                        </div>

                    </form>
                </div>
            </div>


            <p class="copyright"><a href="http://www.beian.miit.gov.cn">赣ICP备17005368号-1</a></p>


        </div>
    </div>
</div>


{include file="common/script" /}
<script src="/assets/js/jquery.min.js"></script>

<script>
    let c;

    // 轮询用户是否已经扫码
    $(document).ready(function () {
        c = setInterval(check_login, 2000);   //每2秒执行一次
    });

    //检测用户是否已扫码
    function check_login() {
        const scene_id = $("input[name='scene_id']").val();
        const ticket = $("input[name='ticket']").val();
        $.ajax({
            url: '/index/checkLogin',
            data: {scene_id: scene_id, ticket: ticket},
            type: 'POST',
            dataType: 'JSON',
            success: function (res) {
                if (res.code === 1) {
                    // 扫码成功
                    // alert(res.msg);
                    window.clearInterval(c); //终止轮询
                    window.location.href = '/index/index';
                }
            }
        })
    }
</script>

</body>
</html>

2、加按钮绑定、解绑

<div class="form-group">
                            <a onclick="unbind_wechat()" class="btn btn-danger btn-sm">解除绑定微信</a>
                            <a href="/index/bind_wechat" target="_blank" class="btn btn-info btn-sm">绑定微信</a>
                        </div>
<script src="/assets/js/jquery.min.js"></script>

<script>
    function unbind_wechat() {
        $.ajax({
            url: '/index/unbind_wechat',
            data: {},
            type: 'POST',
            dataType: 'JSON',
            success: function (res) {
                if (res.code === 1) {
                    alert(res.msg);
                }
            }
        })
    }
</script>

参考文章:

1、PHP tp5微信扫码登陆_tp5扫码登录-CSDN博客

2、【PHP+微信开发】之微信扫码登录_php 微信扫码登录_下页、再停留的博客-CSDN博客

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值