Blog原文:一木林多 - https://www.l5v.cn/archives/95/
近期在做一个项目,甲方爸爸要求平台系统接入微信登录的功能。我寻思着好像微信开发平台好像有这个功能(不是微信公众号平台是微信开发平台)。
但是我去微信开发平台看了一下,要接入网站着实有点麻烦。于是我就放弃了通过微信开发平台接入网站登录的功能。当我在百度上搜索相关解决方案的时候突然发现:可以利用公众号的一些功能来实现甲方爸爸的要求!
1、首先需要准备一个认证的微信服务号(或者测试号)。测试账号的申请地址:
https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
无需公众帐号、快速申请接口测试号直接体验和测试公众平台所有高级接口。
由于项目目前还是在本地localhost的开发环境,我在ngrok里面买了个frp用来访问外网。测试环境下如何安装ngrok的frp,请参考ngrok的官方文档。
我们先来写一个WeChat类以实现一些必要的基本操作。直接贴代码:
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为自己的认证服务号的相关信息。
下一步,我们在前台获取用来登录的二维码。直接贴核心代码,具体细节优化靠自己了。
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 "
$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 = "
%s
0
";
$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获取用户的唯一标识并在数据库中查找。如果查有则登录,查无则无法登录(查无则提示是否注册之类的)。