准备材料:
微信公众号-认证服务号,如果没有可以申请测试号
申请测试号链接https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
交互时序图
权限说明:
①除认证服务号外,其他类型的没有生成带参数二维码的权限
②订阅号有接受事件通知的权限,可接受关注事件并完成后续流程,但关注过的粉丝就无法直接通过扫码使用了。理论上可准备大量新号(话说CSDN很长一段时间用这个操作给订阅号涨粉)。
准备工作
1.设置ip白名单
将ip白名单设置为你的服务器ip,如果在本地测试,则设置为本地外网ip
2.开启服务器配置
接口文档地址:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html
微信会向你填写的服务器地址发送一个get请求,
https://URL?signature=XXX×tamp=XXX&nonce=XXX&echostr=XXX
可直接复制粘贴微信提供的示例代码,或者如果是明文模式,可直接返回echostr,即:
echo $_GET["echostr"];
即可快速通过开发者验证。验证完之后点击右上角的启用。明文模式下,微信只做一次验证,启用后验证代码就可以删除了。
一、获取access_token
GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
不详细解释了,curl一下就好。
建议将appid和appsecret保存在文件中,请求时引入,示例:
wxkey.php内容
$appid='wx************';
$appsecret='**************************';
将获取到的access_token保存,可以保存在数据库redis等等,笔者这里将其写入文件保存。access_token有效期为7200秒,有效期内可以反复使用,过期则需要重新获取。
不应每次调用微信接口都去重新获取,一旦多个服务同时请求,会导致一部分服务的access_token被刷新失效而请求失败。
使用文件保存:
function get_access_token(){
$access_token=file_get_contents(__DIR__."/access_token");
//filectime()函数获取文件最后修改时间
$remainder=time()-filectime(__DIR__."/access_token");
if($remainder < 7200){
$result=$access_token;
}
else {
//curl()不是自带函数,需要自行封装使用
$result=curl("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appid}&secret={$appsecret}");
$file=fopen(__DIR__."/access_token","w");
fwrite($file,$result);
fclose($file);
}
return $result;
}
注意返回内容是一段json,调用时要使用json_decode()将其格式化。
{"access_token":"ACCESS_TOKEN","expires_in":7200}
二、获取带参数二维码(临时)
POST URL: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN
POST内容为一段json:
{"expire_seconds": 604800, "action_name": "QR_STR_SCENE", "action_info": {"scene": {"scene_str": "test"}}}
返回值:
{"ticket":"gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm3sUw==","expire_seconds":60,"url":"http://weixin.qq.com/q/kZgfwMTm72WWPkovabbI"}
这里只要拿到ticket就可以返回给前端去获取二维码了,其他返回值基本没用
function get_qrcode(){
$data='{"expire_seconds": 604800, "action_name": "QR_STR_SCENE", "action_info": {"scene": {"scene_str": "test"}}}';
$access_token=get_access_token($appid,$appsecret);
$access_token=json_decode($access_token)->access_token;
//curl()不是自带函数,需要自行封装使用
$ticket=curl("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={$access_token}",1,$data);
$ticket=urlencode(json_decode($ticket)->ticket);
return $ticket;
前端加载img时,使用js设置其src=“https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET”即可加载二维码图片。
三、接受用户扫码事件通知
用户扫码之后,微信会向你填写的服务器地址POST一段xml,如果忘了请看前面的准备工作。
(笔者吐槽:很恶心,其他接口都是json,为啥这里弄个xml,而且php处理xml比json费劲)
格式如下:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event>
<EventKey><![CDATA[qrscene_123123]]></EventKey>
<Ticket><![CDATA[TICKET]]></Ticket>
</xml>
其中,FromUserName内容是用户id,即openid,EventKey和Ticket对应创建二维码时携带的参数。当服务器收到这个推送的时候就意味该用户已经扫码并且关注公众号了,如果没有关注不会收到推送。
接下来解析xml并保存准备给前端提供查询,笔者这里采用保存到文件的方式,也可以保存到数据库redis等等。
$xml=simplexml_load_string(file_get_contents("php://input"));
$openid="$xml->FromUserName";
$event_key="$xml->EventKey";
$ticket="$xml->Ticket";
$write=json_encode(array("openid"=>$openid,"event_key"=>$event_key));
$file=fopen(__DIR__."/{$ticket}.json","w");
fwrite($file,$write);
fclose($file);
simplexml是php解析xml的一个库,会将xml解析为对象,对象内容必须加上双引号才能转为string,直接引用将会报错;
php://input用于接收header:Content-Type为application/xml、application/json的数据;
(!!!再次吐槽为啥要传xml,json不香吗)
将xml中的openid、event_key、ticket解析出来保存到ticket.json的文件,前端将根据ticket来查询是否扫码。
三、前端轮询获取登陆结果
创建一个查询接口文件:
//ticket下发二维码时已被前端获取
$ticket=$_GET["ticket"];
@$login=file_get_contents(__DIR__."/{$ticket}.json");
if($login){
$openid=json_decode($login)->openid;
session_start();
$_SESSION['username']=$openid;
echo json_encode(array("status"=>"ok"));
}
这里使用session保持会话,也可使用其他方式。
前端js轮询:这里方便ajax使用了jQuery,也可以用别的库如Axious
var ticket;//创建全局变量
$(function(){
//请求登录二维码
$.get("login.php",function(data){
$("#qrcode").attr("src","https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket="+data);
ticket=data;//保存ticket
});
function veri(){
//验证扫码状态
$.get("wx_login_veri.php?ticket="+ticket,//查询接口
function(data){
if(JSON.parse(data).status=="ok"){
window.open("admin.html","_self");
}
}
);
}
var timer=setInterval(function(){
veri();
},3000);
});
设置一个setInterval进行轮询。这里也可以使用websocket技术,但是看了一圈网站都是用的轮询,包括微信、支付宝、CSDN等,可能一个登录页不值得大动干戈。
源码下载(加入了账号密码登录和数据库查询绑定内容以及文中用到的函数封装):
https://www.whsand.com/wx_login_code.zip