在网站或系统中实现扫码关注微信公众号自动注册登录,可以大幅简化用户操作流程,提升用户留存率。下面我们一步步讲解完整的实现方案。

前期准备
首先需要在微信公众平台注册服务号并完成认证,获取到AppID和AppSecret,同时配置好服务器的回调地址,确保公众号能正常推送事件消息。另外需要准备一个MySQL数据库,用来存储用户信息和扫码状态。
数据库表设计
我们需要两张核心表,一张存储用户信息,一张存储临时扫码状态:
-- 用户信息表 CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `openid` varchar(50) NOT NULL COMMENT '公众号用户唯一标识', `nickname` varchar(100) DEFAULT NULL COMMENT '用户昵称', `avatar` varchar(255) DEFAULT NULL COMMENT '用户头像', `create_time` int(11) NOT NULL COMMENT '注册时间', PRIMARY KEY (`id`), UNIQUE KEY `openid` (`openid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 扫码状态表 CREATE TABLE `scan_tmp` ( `id` int(11) NOT NULL AUTO_INCREMENT, `scene_id` varchar(32) NOT NULL COMMENT '临时场景值', `openid` varchar(50) DEFAULT NULL COMMENT '扫码用户openid', `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0未扫码 1已扫码未关注 2已关注', `create_time` int(11) NOT NULL COMMENT '创建时间', PRIMARY KEY (`id`), KEY `scene_id` (`scene_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
生成带参数二维码
用户访问网站登录页时,后端需要生成一个临时场景值,调用微信接口生成对应的带参数二维码,场景值可以存到scan_tmp表中,状态设为未扫码。以下是生成二维码的PHP代码:
<?php
class WechatQrcode {
private $appid;
private $appsecret;
private $access_token;
public function __construct($appid, $appsecret) {
$this->appid = $appid;
$this->appsecret = $appsecret;
$this->getAccessToken();
}
// 获取access_token
private function getAccessToken() {
// 实际开发中建议缓存access_token,避免频繁调用接口
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appid}&secret={$this->appsecret}";
$res = json_decode(file_get_contents($url), true);
$this->access_token = $res['access_token'];
}
// 生成临时带参数二维码
public function createTempQrcode($scene_id, $expire_seconds = 300) {
$url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={$this->access_token}";
$data = [
'expire_seconds' => $expire_seconds,
'action_name' => 'QR_STR_SCENE',
'action_info' => [
'scene' => [
'scene_str' => $scene_id
]
]
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);
// 返回二维码图片地址
return "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" . urlencode($res['ticket']);
}
}
// 使用示例
$appid = '你的公众号AppID';
$appsecret = '你的公众号AppSecret';
$scene_id = uniqid('scan_', true); // 生成唯一场景值
$qrcode = new WechatQrcode($appid, $appsecret);
$qrcode_url = $qrcode->createTempQrcode($scene_id);
// 将scene_id存入scan_tmp表,状态设为0
?>接收公众号关注回调
当用户扫码关注公众号时,微信会向配置的回调地址推送事件消息,我们需要在回调接口中处理这个事件,获取用户的openid,并更新scan_tmp表的状态,同时如果是新用户就完成注册。
<?php
// 公众号回调接口处理
class WechatCallback {
private $appid;
private $appsecret;
public function __construct($appid, $appsecret) {
$this->appid = $appid;
$this->appsecret = $appsecret;
}
// 验证签名
public function checkSignature() {
$signature = $_GET["signature"];
$timestamp = $_GET["timestamp"];
$nonce = $_GET["nonce"];
$token = '你配置的公众号Token';
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr, SORT_STRING);
$tmpStr = implode($tmpArr);
$tmpStr = sha1($tmpStr);
return $tmpStr == $signature;
}
// 处理事件回调
public function handleEvent() {
$postStr = file_get_contents("php://input");
if (!empty($postStr)) {
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
$event = $postObj->Event;
if ($event == 'subscribe') { // 关注事件
$openid = $postObj->FromUserName;
$scene_str = $postObj->EventKey; // 扫码的场景值,格式为qrscene_场景值
$scene_id = str_replace('qrscene_', '', $scene_str);
// 获取用户信息
$user_info = $this->getUserInfo($openid);
// 检查用户是否已存在
$user = $this->checkUserExist($openid);
if (!$user) {
// 新用户,插入用户信息表
$this->insertUser($openid, $user_info['nickname'], $user_info['headimgurl']);
}
// 更新扫码状态表
$this->updateScanStatus($scene_id, $openid, 2);
}
}
}
// 获取用户信息
private function getUserInfo($openid) {
$access_token = $this->getAccessToken();
$url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token={$access_token}&openid={$openid}&lang=zh_CN";
return json_decode(file_get_contents($url), true);
}
// 检查用户是否存在
private function checkUserExist($openid) {
// 这里写查询数据库的逻辑,返回用户信息或null
// 示例省略数据库查询代码
return null;
}
// 插入新用户
private function insertUser($openid, $nickname, $avatar) {
// 这里写插入数据库的逻辑
// 示例省略数据库操作代码
}
// 更新扫码状态
private function updateScanStatus($scene_id, $openid, $status) {
// 这里写更新scan_tmp表的逻辑,将对应scene_id的记录状态更新,绑定openid
// 示例省略数据库操作代码
}
private function getAccessToken() {
// 同之前获取access_token的逻辑,建议缓存
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appid}&secret={$this->appsecret}";
$res = json_decode(file_get_contents($url), true);
return $res['access_token'];
}
}
// 入口处理
$appid = '你的公众号AppID';
$appsecret = '你的公众号AppSecret';
$callback = new WechatCallback($appid, $appsecret);
if (isset($_GET['echostr'])) {
// 验证接口时返回echostr
if ($callback->checkSignature()) {
echo $_GET['echostr'];
}
} else {
$callback->handleEvent();
}
?>前端轮询查询登录状态
前端页面展示二维码后,需要定时向后端发起请求,查询当前场景值对应的扫码状态,如果状态变为已关注,则完成登录操作:
<?php
// 查询扫码状态的接口
$scene_id = $_GET['scene_id'];
// 查询scan_tmp表中对应scene_id的记录
// 如果status为2,说明用户已关注,获取对应的openid,然后生成登录态(比如设置session或返回token)
$scan_record = []; // 这里写查询数据库的逻辑
if ($scan_record['status'] == 2) {
$openid = $scan_record['openid'];
// 生成登录态,比如设置session
session_start();
$_SESSION['user_openid'] = $openid;
// 返回登录成功标识
echo json_encode(['code' => 0, 'msg' => '登录成功']);
} else {
echo json_encode(['code' => 1, 'msg' => '未扫码或未完成关注']);
}
?>注意事项
- 公众号的回调地址需要配置正确,且服务器需要支持80或者443端口,微信推送消息只支持这两个端口。
access_token需要缓存,有效期为7200秒,频繁调用接口会被微信限制。- 临时二维码的有效期可以根据业务需求调整,过期后需要重新生成。
- 用户信息和扫码状态的数据库操作建议加上事务,避免数据不一致。
实际开发中还需要考虑更多异常情况,比如用户扫码后取消关注、网络异常导致回调未收到等问题,需要根据业务场景补充对应的处理逻辑。