资讯动态

微信小程序PHP验证系统:JWT+签名验签实战

发布时间:2026/9/15 5:46:30 来源:尧图企业网站定制
简介这是一套基于微信小程序的网络验证系统源码面向前端开发者、小程序初学者及需要快速搭建用户身份核验功能的技术人员适用于在线票务、预约服务、会员管理等需轻量级验证能力的业务场景。资源共416个文件以140个PHP后端逻辑文件含index.php主入口、api.php接口层、install部署脚本、78个GIF动效资源、64个JS交互脚本、49个HTML页面模板为主辅以Layui框架构建的admin后台含layuiadmin目录及响应式CSS样式集app.min.css、admin.css、mobile.css等整体包体7.64MB结构清晰前后端分离明确。已有31人学习下载读者可直接获取完整可运行的小程序服务端代码、标准化安装流程、管理员与用户双角色功能模块、以及基于Layui的可视化后台界面大幅降低从零开发验证系统的门槛与调试成本。1. 这不是「炸鸡」是微信小程序里跑得最稳的轻量级网络验证入口你打开index.php看到的是一个极简的 HTTP 请求分发器你点开admin/目录里面没有花哨的 RBAC 权限树只有三张表结构清晰的.sql文件和带layuiadmin标识的静态路由配置你调试api.php时发现所有接口返回都强制走json_encode([code0,data...])统一格式——这不是玩具项目而是一套在真实商户场景中已稳定运行超 18 个月的微信小程序网络验证系统。它不依赖云开发、不调用微信云托管纯 PHP MySQL 架构部署在普通 Linux 虚拟主机即可上线。核心逻辑全部收束在interface/下的verify.js和auth.js里前端用wx.request()直连后端用$_POST[sign]做签名验签全程无 session、无 cookie靠openid 时间戳 随机盐生成一次性的验证 token。适合做预约核销、会员扫码认证、活动资格校验等对实时性要求高、但并发峰值不超过 500 QPS 的中小业务场景。2. 前端验证链路从wx.login()到verify.js的三次关键握手2.1 小程序端如何发起首次可信请求微信小程序启动后第一件事不是调wx.getUserInfo()而是执行wx.login()获取临时登录凭证code再通过wx.request()提交到后端/api/login.php注意该路径由api.php中的路由映射定义。这个过程必须在app.js的onLaunch生命周期内完成且需设置header: { content-type: application/x-www-form-urlencoded }否则 PHP 端$_POST无法接收。// pages/index/index.js onLoad() { wx.login({ success: res { wx.request({ url: https://your-domain.com/api.php?actionlogin, method: POST, data: { code: res.code }, header: { content-type: application/x-www-form-urlencoded }, success: resp { if (resp.data.code 0) { wx.setStorageSync(token, resp.data.data.token); wx.setStorageSync(openid, resp.data.data.openid); } } }); } }); }提示api.php中actionlogin对应的处理函数位于API/login.php它会调用WeChat::getOpenid($code)向微信服务器换取openid并生成一个有效期为 2 小时的 JWT token。该 token 的 payload 固定包含openid、exp时间戳、iat签发时间密钥硬编码在config.php的JWT_SECRET常量中不可直接暴露在前端代码里。2.2 验证请求如何携带身份凭证后续所有业务接口如核销码、查会员状态必须在请求头中携带Authorization: Bearer token。verify.js封装了统一的请求拦截器自动读取wx.getStorageSync(token)并注入 header// utils/verify.js function requestWithAuth(options) { const token wx.getStorageSync(token); return wx.request({ ...options, header: { content-type: application/json, Authorization: Bearer ${token} }, fail: err { if (err.errMsg.includes(401)) { // token 过期触发重新登录 wx.removeStorageSync(token); wx.navigateTo({ url: /pages/login/login }); } } }); } module.exports { requestWithAuth };2.2.1 后端如何解析并校验这个 tokenapi.php在收到请求后先调用JWT::decode($token, JWT_SECRET)解析 payload再检查exp是否过期、openid是否存在于user表中。关键逻辑在API/auth.php的checkToken()函数// API/auth.php function checkToken($token) { try { $decoded JWT::decode($token, JWT_SECRET, [HS256]); if (time() $decoded-exp) { throw new Exception(Token expired); } $stmt $pdo-prepare(SELECT id FROM user WHERE openid ?); $stmt-execute([$decoded-openid]); return $stmt-fetch(PDO::FETCH_ASSOC) ? $decoded-openid : false; } catch (Exception $e) { return false; } }注意JWT_SECRET必须在部署时修改为 32 字符以上随机字符串原包中默认值zhajinetwork2023仅作演示上线前必须替换。否则攻击者可伪造任意openid。2.3 移动端适配与 UI 渲染逻辑拆解mobile.css并非简单响应式媒体查询而是针对微信内置浏览器做了三处关键 hack强制禁用-webkit-tap-highlight-color防止点击灰斑用transform: translateZ(0)触发硬件加速解决 iOS 滚动卡顿所有按钮:active状态使用opacity: 0.7而非background-color变化避免 Safari 渲染闪烁。template.css则定义了 7 类标准卡片组件.card-verify,.card-member,.card-scan等每个组件内部结构固定为!-- template/card-verify.wxml -- view classcard-verify view classcard-header text classstatus {{item.status valid ? success : error}}{{item.statusText}}/text /view view classcard-body text classcode{{item.code}}/text /view /view对应 CSS 中.card-verify .status.success使用#4CAF50.card-verify .status.error使用#f44336颜色值直接写死不走 CSS 变量——这是为兼容微信基础库 2.10.4 以下版本所做的降级处理。3. 后端验证核心index.php分发机制与api.php接口路由设计3.1index.php如何成为唯一入口整个系统采用「前端路由后置」模式所有小程序请求均指向index.php由它根据$_GET[r]或$_POST[action]决定执行路径。这种设计规避了 Apache/Nginx 的.htaccess重写依赖适配所有共享主机环境。// index.php define(ROOT_PATH, dirname(__FILE__) . /); require_once ROOT_PATH . config.php; require_once ROOT_PATH . vendor/autoload.php; $action $_POST[action] ?? $_GET[r] ?? home; switch ($action) { case login: require_once ROOT_PATH . API/login.php; break; case verify: require_once ROOT_PATH . API/verify.php; break; case member: require_once ROOT_PATH . API/member.php; break; default: http_response_code(404); echo json_encode([code 404, msg Route not found]); }关键点index.php不做任何业务逻辑只做路由分发。真正的验证逻辑全部下沉到API/目录下各文件便于单元测试和权限隔离。3.2api.php的三层防护体系api.php是实际处理请求的主控文件它构建了三层安全屏障层级防护手段实现位置触发条件L1 网络层IP 白名单限制config.php中ALLOWED_IPS数组仅允许127.0.0.1和微信服务器回调 IP 段101.226.192.0/20,123.151.136.0/22L2 协议层HTTPS 强制跳转index.php开头if ($_SERVER[HTTPS] ! on) { header(Location: https://...); exit; }所有非 HTTPS 请求 301 重定向L3 业务层动态签名验签API/auth.php中verifySign()函数每个 POST 请求必须携带sign参数值为md5(openid timestamp nonce secret)verifySign()函数实现如下// API/auth.php function verifySign($data) { $required [openid, timestamp, nonce, sign]; foreach ($required as $key) { if (!isset($data[$key])) return false; } if (abs(time() - $data[timestamp]) 300) return false; // 5分钟时效 $expected md5($data[openid] . $data[timestamp] . $data[nonce] . API_SECRET); return hash_equals($expected, $data[sign]); }注意API_SECRET与JWT_SECRET是两个独立密钥前者用于接口级防重放后者用于用户身份认证不可复用。nonce由小程序端生成 UUIDv4服务端需记录最近 100 个nonce防止重放原包中该功能未实现需自行补全cache/nonce.php。3.3 数据库表结构与验证原子操作系统仅需三张表支撑全部验证流程结构精简到极致表名字段说明userid,openid,nickname,avatar,created_at用户基础信息openid为主键verify_logid,openid,code,status,created_at,ip每次核销记录code建立唯一索引member_cardid,openid,level,expire_time,updated_at会员等级与过期时间expire_time用于自动降级验证原子操作发生在API/verify.php的doVerify()函数中使用 MySQL 的INSERT ... ON DUPLICATE KEY UPDATE实现幂等// API/verify.php $stmt $pdo-prepare( INSERT INTO verify_log (openid, code, status, ip) VALUES (?, ?, used, ?) ON DUPLICATE KEY UPDATE status used ); $stmt-execute([$openid, $code, $_SERVER[REMOTE_ADDR]]); if ($stmt-rowCount() 0) { // 已存在相同 code 记录说明已被核销 return [code 1002, msg Code already used]; }关键参数说明code字段在verify_log表中设为UNIQUE约束确保同一核销码只能被成功插入一次。ON DUPLICATE KEY UPDATE不会报错而是返回affected_rows0据此判断是否重复提交。4. 管理后台实战LayuiAdmin 的权限收敛与数据看板定制4.1layuiadmin目录下的权限控制逻辑admin/目录并非完整后台框架而是基于 LayuiAdmin v2.5.3 的最小化裁剪版。其权限模型极度简化仅区分管理员role1与操作员role2两级无菜单动态加载所有页面路由硬编码在admin/src/router.js中// admin/src/router.js export default [{ name: dashboard, path: /dashboard, component: ./views/dashboard/index.vue, meta: { title: 仪表盘, icon: fa-home } }, { name: verify-log, path: /verify/log, component: ./views/verify/log.vue, meta: { title: 核销记录, icon: fa-list, role: [1,2] } }, { name: member-list, path: /member/list, component: ./views/member/list.vue, meta: { title: 会员管理, icon: fa-users, role: [1] } }]注意meta.role字段控制菜单可见性但真正的数据权限在后端接口中校验。例如/admin/api/member/list.php会检查$_SESSION[role] 1操作员访问直接返回 403。4.2 数据看板的三个必改配置项admin/src/views/dashboard/index.vue中的数据看板依赖三个外部 API部署时必须修改配置项默认值修改位置说明DASHBOARD_API_BASE/api.php?actionadmin/src/utils/request.js第 12 行改为你的完整域名如https://api.your-domain.com/api.php?actionCHART_DATE_RANGE7admin/src/views/dashboard/index.vue第 89 行表示折线图显示最近 7 天数据可改为30查看月度趋势VERIFY_STATUS_MAP{0:待核销,1:已核销}admin/src/views/verify/log.vue第 42 行若业务需新增「已退款」状态此处同步追加2:已退款4.3 日志导出功能的 SQL 注入防护补丁admin/src/views/verify/log.vue中的导出按钮调用/admin/api/export/log.php原包存在 SQL 注入风险$where status {$_GET[status]};。修复方式是在admin/api/export/log.php开头加入白名单校验// admin/api/export/log.php $status $_GET[status] ?? ; $allowedStatus [0, 1, 2]; // 严格限定可选值 if (!in_array($status, $allowedStatus)) { die(json_encode([code400, msgInvalid status])); } $where status {$status};提示此补丁必须手动添加原包未包含。导出功能使用fputcsv()生成 CSV字段顺序固定为id,openid,code,status,created_atExcel 打开时无需额外编码转换。5. 安全加固与生产部署 checklist从install/到线上可用的 11 步5.1install/目录的隐藏与初始化陷阱install/目录包含init.sql建表语句和setup.php一键初始化脚本但部署完成后必须彻底删除或 403 禁止访问。setup.php的致命缺陷在于它会将数据库密码明文写入config.php且不校验config.php是否已存在。正确做法是手动创建config.php填入?php define(DB_HOST, localhost); define(DB_NAME, zhajinetwork); define(DB_USER, your_db_user); define(DB_PASS, your_strong_password); define(JWT_SECRET, 32_char_random_string_here); define(API_SECRET, another_32_char_secret); define(ALLOWED_IPS, [127.0.0.1, 101.226.192.0/20]); ?执行mysql -u root -p zhajinetwork install/init.sql立即删除install/目录警告若跳过第 1 步直接运行setup.php会导致config.php中密码被覆盖为弱默认值且setup.php无二次确认机制。5.2 Nginx 生产环境最小化配置在nginx.conf的 server 块中必须添加以下四条规则缺一不可# 1. 禁止访问敏感目录 location ~ ^/(install|admin/src|node_modules|\.git|\.svn|\.idea) { deny all; } # 2. 限制 PHP 执行范围仅允许入口文件 location ~ \.php$ { include fastcgi_params; fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root/index.php; # 强制所有 PHP 请求走 index.php } # 3. 防止目录遍历 location ~ ../ { return 403; } # 4. 静态资源缓存 location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ { expires 1y; add_header Cache-Control public, immutable; }5.3 微信小程序后台的三项关键配置进入微信公众平台 → 开发管理 → 开发设置需完成配置项值说明服务器域名https://your-domain.com必须备案且 SSL 有效不能带 www 或路径request 合法域名https://your-domain.com与上一项一致否则wx.request()报 10003 错误webview 业务域名https://your-domain.com若需在小程序内嵌 H5 页面此项必填验证技巧在app.js中加入console.log(domain ok:, wx.canIUse(request))真机调试时查看控制台是否输出domain ok: true。若为false99% 是域名未配置或证书不匹配。5.4 最小化日志监控方案无需 ELK仅用三行 Linux 命令即可监控异常# 实时查看 401token 失效和 403权限不足错误 tail -f /var/log/nginx/your-domain-error.log | grep -E (401|403) # 统计每分钟核销失败次数code1002 awk /POST \/api.php.*code1002/ {print $4} /var/log/nginx/your-domain-access.log | cut -d: -f1,2 | sort | uniq -c | sort -nr # 检查 verify_log 表是否写满超过 10 万条触发告警 mysql -N -s -e SELECT COUNT(*) FROM zhajinetwork.verify_log; | awk $1 100000 {print ALERT: verify_log over 100k}最后一行命令可加入 crontab 每 5 分钟执行一次输出ALERT时自动邮件通知运维。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价