记录一次开发微信登录网站并关注公众号功能的实现方法

# 折腾 # · 2020-09-03

近期在做一个项目,甲方要求平台系统接入微信登录的功能。我寻思着好像微信开发平台好像有这个功能(不是微信公众号平台是微信开发平台)。但是我去微信开发平台看了一下,要接入网站着实有点麻烦。于是我就放弃了通过微信开发平台接入网站登录的功能。当我在百度上搜索相关解决方案的时候突然发现:可以利用公众号的一些功能来实现甲方的要求。

首先需要准备一个认证的微信服务号(或者测试号)。测试账号的申请地址:

https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

无需公众帐号、快速申请接口测试号直接体验和测试公众平台所有高级接口。

由于项目目前还是在本地localhost的开发环境,我在ngrok里面买了个frp用来访问外网。测试环境下如何安装ngrok的frp,请参考ngrok的官方文档。

我们先来写一个WeChat类以实现一些必要的基本操作。直接贴代码:

<?php
Class WeChat{

    protected $appid;
    protected $secret;
    protected $accessToken;

    function __construct(){
        $this->appid       = "公众号的appID";
        $this->secret      = "公众号的appsecret";
        $this->accessToken = $this->getAccessToken();
    }

    /***
     * 获取access_token
     * token的有效时间为2小时,这里可以做下缓存处理,提高效率
     * @return
     **/
    private function getAccessToken(){
        $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=".$this->appid."&secret=".$this->secret;
        $res = json_decode($this->httpRequest($url),true);
        return $res['access_token'];
    }

    /***
     * POST/GET请求
     * @url 请求url
     * @data POST数据
     * @return
     **/
    private function httpRequest($url, $data = ""){
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        if(!empty($data)){    //判断是否为POST请求
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($curl);
        curl_close($curl);
        return $output;
    }

    /***
     * 获取openID和unionId
     * @code 微信授权登录返回的code
     * @return
     **/
    public function getOpenIdOrUnionId($code){
        $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=".$this->appid."&secret=".$this->secret."&code=".$code."&grant_type=authorization_code";
        $data = $this->httpRequest($url);
        return $data;
    }

    /***
     * 通过openId获取用户信息
     * @openId
     * @return
     **/
    public function getUserInfo($openId){
        $url  = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=".$this->accessToken."&openid=".$openId."&lang=zh_CN";
        $data = $this->httpRequest($url);
        return $data;
    }

    /***
     * 生成带参数的二维码
     * @scene_id 自定义参数(整型)
     * @return
     **/
    public function getQrcode($scene_id){
        $url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=".$this->accessToken;
        $data = array(
            "expire_seconds" => 3600, //二维码的有效时间(1小时)
            "action_name" => "QR_SCENE",
            "action_info" => array("scene" => array("scene_id" => $scene_id))
        );
        $result = $this->httpRequest($url, json_encode($data));
        return $result;
    }

    /***
     * 生成带参数的二维码
     * @scene_str 自定义参数(字符串)
     * @return
     **/
    public function getQrcodeByStr($scene_str){
        $url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=".$this->accessToken;
        $data = array(
            "expire_seconds" => 3600, //二维码的有效时间
            "action_name" => "QR_STR_SCENE",
            "action_info" => array("scene" => array("scene_str" => $scene_str))
        );
        $result = $this->httpRequest($url, json_encode($data));
        return $result;
    }

    /**
     * 换取二维码
     * @ticket
     * @return
     */
    public function generateQrcode($ticket){
        return "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=".$ticket;
    }
}

注意需要修改代码中的APPID和APPSECRET为自己的认证服务号的相关信息。

下一步,我们在前台获取用来登录的二维码。直接贴核心代码,具体细节优化靠自己了。

<?php
    header("Content-type:text/html;charset=utf-8");
    require_once('WeChat.class.php');    //加载上一步写的WeChat类文件
    $WeChat = new WeChat();
    $scene_str = "唯一的值";    //这里设置一个唯一值用于后续的登录回调判断
    $result = json_decode($WeChat->getQrcodeByStr($scene_str), true);
    $qrcode = $WeChat->generateQrcode($result['ticket']); //生成二维码
    echo "自定义参数:".$scene_str;
    echo "<br/><img src=\"$qrcode\">";    //$qrcode为二维码的具体链接
?>

至此我们就能成功得到一个唯一的二维码提供给用户来扫描了。

下一步,当用户扫描了我们提供的二维码之后,公众号会向API发送一条消息。在处理公众号发送的回调消息之前,我们需要编写一个API并修改公众号后台的接口配置信息。

<?php 
define("TOKEN", "123456");    //这里的TOKEN就是公众号后台接口的TOKEN,可以自定义

$wechatObj = new wechatCallbackapiTest();
$wechatObj->responseMsg();
$wechatObj->valid();
class wechatCallbackapiTest {
    
    //响应公众号的请求
    public function valid() {
        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"];
        $echoStr = $_GET["echostr"];
        if ($this->checkSignature($signature, $timestamp, $nonce)) {
            echo $echoStr;
            exit;
        }
    }
    
    //签名验证方法
    private function checkSignature($signature, $timestamp, $nonce) {
        $token = TOKEN;
        $tmpArr = array($token, $timestamp, $nonce);
        sort($tmpArr, SORT_STRING);
        $tmpStr = implode($tmpArr);
        $tmpStr = sha1($tmpStr);
        if ($tmpStr == $signature) {
            return true;
        } else {
            return false;
        }
    }
    
    //获取公众号返回的信息
    public function responseMsg() {
        $postStr = file_get_contents('php://input');
        if (!empty($postStr)) {
            $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
            $postObj = json_encode($postObj);
            $postObj = json_decode($postObj);
            //我们可以使用file_put_contents的方法输出公众号提交的内容
            file_put_contents("1.txt", json_encode($postObj));
            $fromUsername = $postObj->FromUserName;
            $toUsername = $postObj->ToUserName;
            $time = time();
            //此处为登录验证的核心代码
            //此处为登录验证的核心代码
            //此处为登录验证的核心代码
            //此处为登录验证的核心代码
            //此处为登录验证的核心代码
            $textTpl = "<xml>
                                <ToUserName><![CDATA[%s]]></ToUserName>
                                <FromUserName><![CDATA[%s]]></FromUserName>
                                <CreateTime>%s</CreateTime>
                                <MsgType><![CDATA[%s]]></MsgType>
                                <Content><![CDATA[%s]]></Content>
                                <FuncFlag>0</FuncFlag>
                                </xml>";
            $msgType = "text";
            //登录成功返回的文本
            $contentStr = "登录成功!";
            $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
            echo $resultStr;
        } else {
            echo "";
            exit;
        }
    }
}

我们要对用户扫描二维码后公众号向API发送的消息进行分析。以下是经过json_encode处理过的消息。

{
    "ToUserName":"xxxxxxx",
    "FromUserName":"xxxxxxxxxxx",
    "CreateTime":"1599098138",
    "MsgType":"event",
    "Event":"SCAN",
    "EventKey":"key123",
    "Ticket":"xxxxxxxxx"
}

其中ToUserName指的是公众号的账号,FromUserName指的是用户的唯一标识,Event有两个值,subscribe指首次关注,scan为扫码(即已经关注过了),EventKey即我们在前面定义的scene_str唯一值。

实现到这个步骤,总有思路如何实现登录验证的核心代码了吧!?

具体验证代码不好贴出,在这里提供一个完整思路:当用户扫描二维码时,公众号向API发送消息,API获取用户的唯一标识并在数据库中查找。如果查有则登录,查无则无法登录(查无则提示是否注册之类的)。

如无特殊说明,本博所有文章均为博主原创。

如若转载,请注明出处:一木林多 - https://www.l5v.cn/archives/95/

评论