个人小程序实现扫码登录,30元/年搞定
自己博客很早就接入了QQ登录、新浪博客登录、GitHub登录,唯独缺少微信登录。以前实现微信登录需要企业认证(300元/年),最近研究小程序时发现:个人认证的小程序也可以实现扫码登录,只需30元/年,值得研究!
博客不希望游客随意评论,但也要方便用户登录,提升体验——微信扫码是目前最方便的方式。
方案一:微信开放平台扫码登录 企业专属
PC网页端扫码,官方体验最佳,需企业认证
✅ 官方支持,体验好 ✅ 用户信任度高 ✅ 可获取昵称、头像、性别 | ❌ 需企业认证(300元/年) ❌ 需要域名备案 ❌ 审核较严格 |
方案二:微信公众号扫码登录 不推荐
同样需要企业认证,300元/年,个人开发者不划算。
方案三:微信小程序扫码登录 ✅ 推荐个人
个人认证即可,30元/年,实现简单,扫码即登录
✅ 个人即可使用 ✅ 费用低(30元/年) ✅ 实现简单 ✅ 扫码即登录 | ❌ 小程序 ❌ 部分接口需认证小程序 ❌ 仅能获取 openid |
说明
小程序方案的缺点是不支持获取昵称、头像、性别等详细信息,但对于博客评论登录场景来说,openid 已经完全够用。
整个扫码登录涉及三端协作:Web前端、后端API、微信小程序,流程如下:
┌─────────┐ ┌─────────┐ ┌─────────┐ │ Web端 │ │ 后端API │ │ 小程序 │ │ │ │ │ │ │ │ 生成scene├─────>│ 创建记录│ │ │ │ │ │ │ │ │ │ 显示二维码│<─────│ 返回scene│ │ │ │ │ │ │ │ │ │ 轮询状态 ├─────>│ 查询状态│ │ │ │ │ │ │<─────│ 扫码 │ │ │ │ │─────>│ 登录处理 │ │ │ │ 更新状态 │<─────│ 返回成功 │ │ 登录成功 │<─────│ 返回用户│ │ │ └─────────┘ └─────────┘ └─────────┘
微信小程序账号(mp.weixin.qq.com),个人认证即可
已备案域名
小程序完成备案
1 获取小程序码,直接显示在前端页面(不保存到服务器)

单独做一个登录界面,显示登录二维码
前端的主要代码
<div class="container">
<div class="card border-0 shadow-sm mb-3">
<div class="card-body">
<div class="login-body">
<div data-bs-target="wechat">
<h4 class="text-center">微信登录</h4>
<div class="qr-image" data-bs-qrcode="">
<img style="width: 100%;" src="{$image}">
</div>
<!-- <h5>请使用微信,扫码登录</h5> -->
<h5><p class="status-text waiting" id="statusText">请使用微信扫描二维码</p></h5><br>
<div id="refreshBtn" style="text-align: center;display: none;"><button class="refresh-btn" onclick="location.reload()">
刷新二维码
</button></div>
</div>
<h6>其他登录方式</h6>
<div class="social-links">
<!-- <a data-bs-selector="wechat" href="#wechat" hidden="hidden">
<i class="iconfont icon-weixin "></i>
</a> -->
<a data-bs-selector="mobile" title='QQ登录' href="https://hotxf.com/Home/User/oauth_login/type/qq">
<i class="iconfont icon-mobile "></i>
</a>
<a title='Sina登录' href="https://hotxf.com/Home/User/oauth_login/type/sina">
<i class="iconfont icon-qq "></i>
</a>
<a title='GitHub登录' href="https://hotxf.com/Home/User/oauth_login/type/github">
<i class="iconfont icon-github "></i>
</a>
</div>
</div>
</div>
</div>
</div>2 实现轮询机制,定时查询扫码状态
轮询代码
<script type="text/javascript">
// 配置
const API_BASE = 'https://hotxf.com/api/wx/'; // 改成你的域名
let scene = '{$token}';
let pollTimer = null;
let pollCount = 0;
// 开始轮询
startPoll();
// 轮询检查状态
function startPoll() {
pollTimer = setInterval(async () => {
pollCount++;
// 5分钟后自动停止
if (pollCount > 150) {
stopPoll();
updateStatus('error', '二维码已过期,请刷新重试');
document.getElementById('refreshBtn').classList.add('show');
return;
}
try {
const response = await fetch(API_BASE + 'check?token=' + scene);
const result = await response.json();
if (result.code === 0) {
const status = result.status;
switch (status) {
case 0:
updateStatus('waiting', '等待扫码...');
console.log('轮询错误:0');
break;
case 1:
console.log('轮询错误:1');
updateStatus('scanned', '已扫码,请在手机确认');
break;
case 2:
console.log('轮询错误:2');
updateStatus('success', '登录成功!正在跳转...');
document.location = '/';
// 授权成功
stopPoll();
break;
case 3:
console.log('轮询错误:3');
stopPoll();
updateStatus('error', '二维码已过期');
document.getElementById('refreshBtn').classList.add('show');
break;
}
} else {
stopPoll();
updateStatus('error', result.msg || '查询失败');
}
} catch (error) {
console.error('轮询错误:', error);
}
}, 2000);
}
// 停止轮询
function stopPoll() {
if (pollTimer) {
clearInterval(pollTimer);
pollTimer = null;
}
}
// 更新状态
function updateStatus(status, text) {
const statusText = document.getElementById('statusText');
statusText.className = 'status-text ' + status;
statusText.textContent = text;
}
// 页面离开时停止轮询
window.addEventListener('beforeunload', stopPoll);
</script>3 扫码成功后自动完成登录跳转

1 创建生成二维码记录,返回 scene 和二维码信息
后端生成二维码信息(由于我有两个后端,博客后端用的Thinkphp3.2一直没有升级,小程序用的Thinkphp8,代码写法不一样,仅供参考)
//微信小程序登录二维码
public function generate(){
$scene = I('scene/s', 's'. substr(md5(uniqid(mt_rand(), true)), 0, 16));
$page = I('page/s', 'pages/scan-login/index');
// 从缓存获取(快速)
$cacheScan = S('scan');
// 检查是否过期
if ($cacheScan['status']===0) {
return json_encode(['image'=>$cacheScan['image'],'scene'=>$cacheScan['scene']],JSON_UNESCAPED_SLASHES);
}
// 获取 AccessToken
$accessToken = $this->access_token;
if (!$accessToken) {
echo json_encode(['code' => 500, 'msg' => '获取token失败'], JSON_UNESCAPED_SLASHES);exit;
}
// 调用微信接口生成小程序码
$url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={$accessToken}";
$postData = [
'scene' => $scene, // 场景值,最大32个可见字符
'page' => $page, // 页面路径
'env_version' => 'release', // release-正式版 trial-体验版 develop-开发版
'width' => 430, // 二维码宽度
'auto_color' => false, // 自动配置线条颜色
'line_color' => [ // 线条颜色
'r' => 0,
'g' => 0,
'b' => 0
],
'is_hyaline' => false, // 是否透明
];
$result = $this->httpPost($url, json_encode($postData));
if (isset($result['errcode']) && $result['errcode'] != 0) {
echo json_encode(['code' => 500, 'msg' => $result['errmsg']], JSON_UNESCAPED_SLASHES);exit;
}
//创建扫码记录,写入到数据库
$db = M('wx_scan_login', 'xf_', 'DB_CONFIG_XF');
$expireTime = date('Y-m-d H:i:s', time() + 300);//有效时间5分钟
$data = [
'scene_str' => $scene,
'status' => 0,// 未扫码
'expires_time' => $expireTime,
'create_time' => time(),
'update_time' => time(),
];
$a = $db->add($data);
if($a>0){
// 1. 缓存记录(用于快速查询)
S('scan_' . $scene, array(
'status' => 0,
'openid' => '',
'user_id' => 0,
), 300);
// 2. 缓存二维码(用于快速查询)
S('scan', array(
'scene' => $scene,
'image' => 'data:image/png;base64,' . base64_encode($result),
'status' => 0,
), 300);
}
return json_encode(['image'=>'data:image/png;base64,' . base64_encode($result),'scene'=>$scene],JSON_UNESCAPED_SLASHES);
}2 提供状态查询接口,供前端轮询
前端轮询代码
/**
* 查询扫码状态(前端轮询)
* GET /api/wx/check
*/
public function check()
{
$scene = I('token/s', '');
if (empty($scene)) {
echo json_encode(['code' => 400, 'msg' => '缺少参数'], JSON_UNESCAPED_SLASHES);exit;
}
// 从缓存获取(快速)
$cacheData = S('scan_' . $scene);
if ($cacheData['status']===0 ) {
echo json_encode(['code'=>0,'msg'=>'success','status'=>$cacheData['status'],'user_id'=>$cacheData['user_id'] ?? 0], JSON_UNESCAPED_SLASHES);exit;
}
// 从数据库获取
$record = M('wx_scan_login', 'xf_', 'DB_CONFIG_XF')->where(array('scene_str'=>$scene))->find();
if (!$record) {
echo json_encode(['code' => 404, 'msg' => '二维码不存在或已过期'], JSON_UNESCAPED_SLASHES);exit;
}
// 检查是否过期
if ($record['expires_time'] < date('Y-m-d H:i:s')) {
$record->status = 3;//3已过期
M('wx_scan_login', 'xf_', 'DB_CONFIG_XF')->where(array('scene_str'=>$scene))->save(array('status'=>3));
echo json_encode(['code' => 404, 'msg' => '二维码不存在或已过期', 'status' => intval(3)], JSON_UNESCAPED_SLASHES);exit;
}
if($record['status']==2){
// 查找或创建用户
$data = M('OauthUser')->where(array('openid'=>$record['openid']))->find();
// 组合存session的数据
$login_info=array(
'id'=>$data['id'],
'head_img'=>$data['head_img'],
'nickname'=>$data['nickname'],
);
if($data['id']>0){
session('user',$login_info);
//写入cookie可保持1年登录状态,但是要把ID加密下,+777,等到用的时候-777
$login_info['id']=$id+777;
cookie('sjd',$login_info,60*60*24*365);
}
// 跳转到登录前的页面
$this_url=empty($_COOKIE['this_url']) ? '/' : cookie('this_url');
}
//登录成功处理
echo json_encode(['code' => 0, 'msg' => 'success', 'status' => intval($record['status']),'url'=>$this_url], JSON_UNESCAPED_SLASHES);exit;
}3 接收小程序回调,更新登录状态并返回用户信息
小程序扫码后,只需要回调服务器接口,告诉服务器openid和scene就可以了,进行登录成功操作
1 添加扫码状态显示页面
生成二维码时要写扫码状态显示页面的路径:
pages/scan-login/index
2 用户扫码后展示确认界面
扫码成功,WEB端网站就自动登录成功了
3 回调后端 API,通知服务器登录成功
回调服务器接口,告诉服务器openid和scene就可以了
如有问题欢迎留言交流
本文为 小风原创文章,转载无需和我联系,但请注明来自 小风博客www.hotxf.com