简介:一套面向本地共享wifi运营场景的小程序独立版源码,适合打算快速搭建自主共享wifi平台的团长、拓展员、商家或个人开发者使用。包内共2004个文件,以749个php后端脚本、140个js逻辑文件、124个json配置及24个css样式为主,并辅以图片、markdown文档与少量视频,整体压缩包约68MB,目录结构较完整,便于按功能模块查阅。已有198人学习下载。源码不仅覆盖小程序搭建与参数配置所需的技术细节,且无需从零开发,按教程即可完成部署;方案还涉及硬件选购(如打印机型号选择)和设备下载部署等线下环节的指导。运营层面则围绕市场定位、用户引导、活动策划与会员管理提供策略参考,帮助整合团长、拓展员、商家资源,从零构建并持续运营本地化共享wifi生态,是一份技术搭建与商业落地兼备的实用参考。
1. WIFI共享小程序独立版源码到底解决什么问题:从扫码连网到数据自持
店铺老板不想把WiFi密码反复报给客人,前台也不愿意每人问一次就答一次。把WiFi密码放到小程序后台,客人扫桌上的码,小程序自动调起系统连接WiFi,这是市面上大多数“WiFi共享小程序”的用法。但它有个绕不开的问题:平台版数据都放在服务商那里,热点位置、连接记录、密码明文全是别人说了算。独立版源码就是为了把这一层拿回来——前端、后端、数据库全部自己部署,密码自己存,连接记录自己看,出了异常可以翻日志排查。
适合谁做这件事呢?一类是手上有一台云服务器、会一点后端接口阅读能力的小团队,想给客户或门店做一套可定制品牌的小程序;另一类是公司内部访客网络管理,需要记录谁在什么时间连过WiFi。如果你只是个人想连自家路由器,用官方App就够了。独立版源码的投入在于服务器、域名、微信小程序认证,还有后续的接口维护,但它换来的是数据可控、页面可改、运营规则自己定。
2. 独立版源码的技术构成:uniapp前端、接口服务与数据库表怎么选
2.1 前端为什么选uniapp:一次开发覆盖微信小程序、H5和管理后台
做WiFi共享小程序,最容易踩的第一个坑就是只盯着微信小程序写。等到要做管理员页、做用户协议页、做客服消息跳转时,你会发现同样的逻辑要再写一套H5。uniapp在这类项目里的优势是用Vue语法写一套代码,同时编译到微信小程序、H5以及其他平台,连“微信小程序 vs Android/iOS/鸿蒙”的端差异都只在条件编译里处理,不需要维护三套工程。
从源码角度看,uniapp项目的页面结构是pages目录下每个模块一个文件夹,stores里放状态管理,common里放请求封装。这套结构对WiFi共享场景足够清楚:用户端页面放在pages/connect,管理员页面放在pages/admin,公共API封装在utils/request.js。真机上跑通微信小程序后,需要做管理后台网页时直接再编译一次H5版,不用另起炉灶。
2.2 后端接口服务的选型:Flask还是Node.js,独立版为什么建议分开部署
WiFi共享小程序的后端业务不重,核心就是提供热点列表、校验密码、记录连接行为。常见做法是用Python Flask或Node.js Express写一组REST接口,数据库用MySQL。选Flask的原因是这个场景的接口不超过十个,用Flask写起来直接,一套路由对应一个热点查询,不需要引入重型框架;但如果你团队更熟JavaScript,用Node.js也完全成立,关键是接口要保持同样语义,前端不用感知后端语言。
我建议把后端和小程序前端分开部署,而不是塞进同一个服务里。原因是小程序前端只走HTTPS域名,后端接口要配SSL证书,分开部署时证书出问题只会影响API,不会连累页面资源;另外后端未来要加管理员登录、设备下线、导出记录这些功能时,独立服务也更好扩展。独立版源码的“独立”二字就体现在这里,它不是套在某个小程序平台里的配置项,而是一个自己控制的接口服务。
2.3 数据库三张核心表:热点表、连接记录表、管理员表
WiFi共享小程序的数据模型可以压缩到三张表。第一张是热点表hotspot,记录每个WiFi点的SSID、加密方式、密码密文、门店名称、备注、启停状态;第二张是连接记录表connect_log,记录哪个用户通过哪个热点连接、连接时间、客户端类型;第三张是管理员表admin,存账号、密码哈希、角色。热点表和服务商的平台版最大的区别是:你可以随时改某一家门店的WiFi名而不影响其他店,这就是独立部署的灵活性。
这里有个容易忽略的字段:hotspot表里要留一个share_code,用来生成扫码参数。用户扫码进入小程序时,前端只拿到这个短码,再向后端换真正的SSID和密码,这样二维码贴在桌上不怕被人扫走密码。连接记录表里也要加一个expire_at字段,用于做访客WiFi的定时下线,这是平台版基本不会给你开放的能力。
2.4 源码目录的大致结构与改动边界
拿一套独立版源码后,先别急着改代码,按目录把边界分清。前端uniapp项目的src下,pages是页面,utils是请求封装,static是静态图;后端服务按flask route分文件,app.py是入口,models.py是表结构,config.py是环境配置。数据库初始化脚本通常在sql目录,里面是建表和种子数据。
改动边界我个人一般这样划:能不改的尽量不动,尤其是请求封装和登录态逻辑。小程序端的登录态、请求头携带token、错误码统一处理这几块,是整个项目最容易改坏的地方,一旦改错,所有页面都会跟着报错。你要改的核心其实只有两处:一处是后端config.py的数据库连接和密钥,一处是前端utils/request.js里的baseURL。其他页面级定制等跑通后再动。
3. 把WIFI共享小程序跑起来:初始化数据库、启动接口并发版小程序
3.1 初始化数据库:建库建表SQL与字段说明
拿到源码第一件事不是打开小程序,而是先把数据库建起来。下面这个SQL按照常见独立版结构整理了最小表集合,字段名做了精简,但语义和大多数实现一致:
CREATE DATABASE IF NOT EXISTS wifi_share DEFAULT CHARACTER SET utf8mb4; USE wifi_share; CREATE TABLE hotspot ( id INT AUTO_INCREMENT PRIMARY KEY, ssid VARCHAR(64) NOT NULL COMMENT 'WiFi名称', password_cipher VARCHAR(256) NOT NULL COMMENT 'AES加密后的密码', encrypt_key_id VARCHAR(32) NOT NULL COMMENT '密钥版本', shop_name VARCHAR(128) DEFAULT '' COMMENT '门店名称', share_code VARCHAR(32) NOT NULL UNIQUE COMMENT '扫码短码', enabled TINYINT(1) DEFAULT 1 COMMENT '是否启用', expire_seconds INT DEFAULT 0 COMMENT '单次连接有效期,0为不限', created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB; CREATE TABLE connect_log ( id INT AUTO_INCREMENT PRIMARY KEY, hotspot_id INT NOT NULL, openid VARCHAR(64) NOT NULL COMMENT '微信用户标识', ssid VARCHAR(64) NOT NULL, client_type VARCHAR(16) DEFAULT '' COMMENT 'Android/iOS/devtools', connected_at DATETIME DEFAULT CURRENT_TIMESTAMP, expire_at DATETIME DEFAULT NULL COMMENT '到期时间' ) ENGINE=InnoDB; CREATE TABLE admin ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(32) NOT NULL UNIQUE, password_hash VARCHAR(128) NOT NULL, role VARCHAR(16) DEFAULT 'admin' ) ENGINE=InnoDB;这段SQL里有三个设计意图要注意。密码字段叫password_cipher,存的是加密后的密文而不是明文,后面接口返回时才解密,避免数据库泄露直接带走密码;share_code加了唯一索引,因为扫码场景里前端只传这个码,不允许重复;connect_log里单独记client_type,排查问题时有非常关键的作用——你会发现开发工具、Android、iOS在连接WiFi上的行为完全不一样,没有这个字段很难定位日志。
3.2 启动后端接口:Python Flask示例与关键配置
数据库就绪后,后端服务用Flask跑一组最小接口。这里演示热点换密码和记录连接行为两个核心接口,其他登录、管理类接口按同样模式扩展:
# app.py from flask import Flask, jsonify, request from flask_cors import CORS import pymysql, os, time, base64 from cryptography.fernet import Fernet app = Flask(__name__) CORS(app) # 开发期放开跨域,上线前收紧 DB_CONFIG = { "host": os.getenv("DB_HOST", "127.0.0.1"), "user": os.getenv("DB_USER", "root"), "password": os.getenv("DB_PASSWORD", "your_password"), "database": "wifi_share", "charset": "utf8mb4", } def get_db(): return pymysql.connect(**DB_CONFIG, cursorclass=pymysql.cursors.DictCursor) @app.route("/api/hotspot/info", methods=["GET"]) def hotspot_info(): share_code = request.args.get("code", "") db = get_db() try: with db.cursor() as cur: cur.execute( "SELECT id, ssid, password_cipher, shop_name, expire_seconds " "FROM hotspot WHERE share_code=%s AND enabled=1", (share_code,) ) row = cur.fetchone() if not row: return jsonify({"code": 404, "msg": "热点不存在或已停用"}) cipher = Fernet(os.getenv("ENCRYPT_KEY", "").encode()) password = cipher.decrypt(row["password_cipher"].encode()).decode() return jsonify({ "code": 0, "data": { "ssid": row["ssid"], "password": password, "shopName": row["shop_name"], "expireSeconds": row["expire_seconds"], } }) finally: db.close() @app.route("/api/connect/log", methods=["POST"]) def connect_log(): body = request.get_json() db = get_db() try: with db.cursor() as cur: cur.execute( "INSERT INTO connect_log (hotspot_id, openid, ssid, client_type) " "VALUES (%s, %s, %s, %s)", (body["hotspotId"], body["openid"], body["ssid"], body.get("clientType", "")) ) db.commit() return jsonify({"code": 0}) finally: db.close() if __name__ == "__main__": app.run(host="0.0.0.0", port=8000)接口逻辑不复杂,但有三处参数直接决定线上能不能用。hotspot_info接口用share_code换WiFi信息,而不是把全部热点返回前端,这样二维码泄露了也只是泄露一个短码,攻击者拿到短码后还要过这层接口才能换到密码。密码解密用的ENCRYPT_KEY从环境变量读取,不要写死在代码里。connect_log写入前没有校验hotspot_id,生产环境建议加一层鉴权,至少校验openid是否为当前登录用户,否则任何人可以伪造连接记录刷爆数据库。
3.3 用微信开发者工具打开小程序前端并配置request域名
后端启动后,把uniapp工程导入微信开发者工具。第一步修改utils/request.js里的baseURL,域名就是你后端服务的HTTPS地址,注意必须是HTTPS且已经在微信小程序后台配置过request合法域名。开发者工具里默认不校验合法域名可以先把请求跑通,但真机预览必须开启“不校验合法域名”才能调试,正式发布前一定关闭。
配置域名在小程序后台的“开发管理-服务器域名”里操作,request合法域名填接口域名,uploadFile合法域名如果有上传图片需求也要加。这里有个容易忽略的细节:域名不能带端口号,必须是标准443端口;如果后端跑在8000端口,需要在前面加一层Nginx把80和443转发到8000,否则小程序后台拒绝保存域名。
3.4 用真机预览验证扫码流程
开发者工具里能跑通不代表真机能用,WiFi类功能尤其如此。用uniapp编译到微信小程序后点“预览”,会生成一个二维码,用测试手机扫码打开。手机上先确认能进入热点详情页,能看到从后端换回来的SSID和密码,再点击连接按钮验证系统弹窗。
这一步最容易出现的问题不是代码报错,而是权限链路没走通。Android端微信需要定位权限才能扫描附近的WiFi,iOS端首次连接要在系统设置里确认加入网络。所以真机预览时准备两台手机,一台Android一台iOS,把两端行为都记录下来再继续往下调。
3.5 本地抓包排查接口返回
如果真机上页面拿不到WiFi信息,用抓包工具看请求链路。小程序请求走的是HTTPS,常规做法是电脑上开抓包工具,手机代理到电脑,再把抓包工具的根证书装到手机上,这样能看到小程序发出去的每个请求和返回体。注意抓包只用于联调阶段,证书装到手机上本身有风险,联调完要卸掉描述文件。
抓包重点看三件事:请求头有没有带token、接口返回的JSON是否符合前端预期、后端日志里有没有抛异常。大多数“前端白屏”问题的根因不是小程序代码,而是后端返回的data字段名和前端不一致,比如后端返回ssid,前端读的是wifiName,字段对不上就会一直连接失败。
4. 小程序连WiFi的核心API:connectWifi参数、系统权限与扫码分享的边界
4.1 连接WiFi的标准调用顺序:startWifi与connectWifi参数详解
微信小程序连WiFi的能力来自wx.startWifi和wx.connectWifi两个接口,顺序不能颠倒。先调用wx.startWifi初始化WiFi模块,等success后再调wx.connectWifi传入SSID和密码。直接调connectWifi在部分Android机型上会报“未初始化”错误,这是文档里不太会强调的坑。一次完整调用示意如下:
// pages/connect/index.js connectWifi() { const { ssid, password } = this.data.hotspot; wx.startWifi({ success() { wx.connectWifi({ SSID: ssid, password: password, success(res) { wx.showToast({ title: '已连接', icon: 'success' }); wx.getConnectedWifi({ success(res) { this.recordConnectLog(res.wifi.ssid); }.bind(this), }); }, fail(err) { wx.showToast({ title: '连接失败,请手动连接', icon: 'none' }); }, }); }, fail() { wx.showToast({ title: 'WiFi模块初始化失败', icon: 'none' }); }, }); }这段代码对参数有严格要求。startWifi不带任何参数,只需要保证在connectWifi之前调用;connectWifi的SSID是必选,password在目标WiFi是开放网络时为空字符串,加密网络必须传明文密码;success回调里拿到的是“已发连接指令”而不是“系统已连上”,所以紧接着调wx.getConnectedWifi去确认状态。实际开发中,iOS端即使connectWifi返回success,系统也可能弹窗询问是否加入,这个弹窗无法在小程序里控制。
4.2 iOS与Android在自动连WiFi上的行为差异
不同端对WiFi API的支持程度差异非常大,这是共享小程序最需要提前知晓的边界。Android端在微信授权定位后,可以做到用户点击按钮直接发起连接,系统弹一次确认窗,同意后即连上;iOS端则限制更多,当目标WiFi不是当前路由时,小程序无法直接连接,只能引导用户跳到系统设置页手动选择网络。
处理iOS的常见做法是调用wx.openSystemSetting或wx.openSetting打开系统设置页,并在页面里展示步骤引导。更现实的方案是:检测到iOS时,把密码复制到剪贴板,同时提示用户去系统设置的WiFi列表里找到对应SSID粘贴密码。不要试图在iOS上做全自动连接,微信不会放开这个能力,硬做只会换来用户在审核和真机上的双重抱怨。
4.3 动态设置顶部标题与缓存过期时间
共享小程序的服务对象是门店,门店希望在扫码进入时能看到“欢迎光临XX店”而不是默认的小程序名。这需要在页面onLoad里根据后端返回的门店名调用wx.setNavigationBarTitle,把顶部标题改成实际店名。这个接口简单,但要注意时机,必须在请求到数据后再设置,否则会闪一下默认标题。
密码在端上的保存也要控制生命周期。不要去localStorage里长期存WiFi密码,因为小程序缓存一旦被读取,抓到包就能看到明文密码。常见做法是设置一个5到10分钟的缓存时间,用wx.setStorageSync存入ssid和password,同时记录一个expireTimestamp,每次使用前校验当前时间是否超过缓存时间,超过就重新向后端请求。微信小程序设置缓存时间有三个可选级别:内存变量、storage、storageSync,推荐用storageSync,因为页面onLoad重新执行时能直接同步读出来。
4.4 扫码进入后的参数解析与防伪造
共享小程序的主要入口是扫桌上的二维码,这个二维码是小程序码而不是普通二维码,扫进去后通过场景值拿到热点标识。小程序码的scene参数有长度限制,只能传32位以内的可见字符,所以后端生成share_code时不要用UUID长串,用类似门店ID加随机数的短码,比如S1001A8F2。
防伪造的关键是share_code本身不能携带密码信息,它只是一个索引。攻击者即使抓包拿到这个码,也只能请求到对应热点的密码,而密码密文由后端统一管理。后端在返回密码前还应该做频率限制,同一share_code在短时间内被不同openid频繁请求就触发告警,避免有人用脚本批量扫热点密码。
4.5 连接记录上报与设备识别
连接成功后,前端要把这次行为上报给后端,这是独立版相对于平台版的核心数据资产。上报时不能只传hotspotId,还要传clientType,因为开发者工具模拟器的WiFi环境和真机完全不同,字段区分能帮你从日志里快速筛出问题设备。clientType的取值可以按平台判断,uni.getSystemInfoSync().platform返回android或ios,开发者工具环境返回devtools。
上报时机放在wx.getConnectedWifi确认已连接之后,不要放在connectWifi的success回调里。原因是connectWifi成功只代表指令发出去了,未必真连上;而getConnectedWifi成功后基本可以确认当前已处于目标网络。上报请求失败不要阻塞用户操作,用静默发送,后端写日志就好,下次连接时再用新的记录覆盖。
5. WIFI共享小程序常见坑与排查:从开发者工具到真机的五条踩坑记录
5.1 现象:iOS点击“连接WiFi”后没有弹出系统连接确认
客户端在iOS上点了连接按钮,页面提示“已连接”,但WiFi列表里根本没有这个网络,系统也没弹任何确认框。反复点击几次后,偶尔能弹一次,但大部分时间无反应。
原因:iOS对私有WiFi网络的连接有系统级限制,wx.connectWifi在iOS上只能连接当前已经连上的路由,或者切换到系统设置页手动操作。Android上那一套“传参-回调-success”的流程在iOS里并不完整生效,success回调只能说明参数被接受了。
解决:代码里做平台判断,iOS端不调用connectWifi,改为把密码复制到剪贴板,调用wx.setClipboardData,然后引导用户打开系统设置页。页面提示文案写成“请选择与小程序提示相同的WiFi名称,粘贴密码连接”,这一步虽然绕,但能覆盖90%以上的iOS用户。
5.2 现象:Android连上了WiFi,但小程序里getConnectedWifi拿不到ssid
Android真机上系统WiFi列表已经显示已连接,但wx.getConnectedWifi返回fail,或者ssid是空字符串。开发者工具上一切正常,一到真机就翻车。
原因:Android 10及以上版本限制了应用读取已连接WiFi信息,微信小程序也没有过多豁免,必须在用户授权定位权限后才能拿到ssid。很多Android rom还会二次弹窗询问“是否允许微信获取位置信息”,用户拒绝后小程序就拿不到任何WiFi信息。
解决:调用getConnectedWifi之前,先检查定位权限。小程序端没有直接的“申请定位权限”接口,需要触发wx.getLocation并用它的授权流程,或者引导用户到小程序设置页打开位置权限。同时做好fail分支:拿不到ssid不代表没连上,直接把连接记录上报里的ssid字段留空,后端再通过热点ID关联,不影响业务。
5.3 现象:开发者工具里所有请求都fail,真机却能通
在微信开发者工具里打开项目,接口请求全部报“request:fail”,Network面板里看不到任何响应;但同一台电脑上用真机预览,接口正常返回数据。
原因:开发者工具的“不校验合法域名”开关默认关闭,而本地联调时接口域名往往是http://192.168.x.x:8000,或者后端只监听本地端口没有开启HTTPS。微信小程序要求正式环境必须HTTPS,开发者工具在没关闭域名校验时直接拦截了请求。
解决:联调阶段在开发者工具详情设置里勾选“不校验合法域名、web-view(业务域名)、TLS版本以及HTTPS证书”,再把baseURL指向局域网地址。如果要真机联调,在手机微信的调试菜单里同样开启“不校验合法域名”。上线前恢复baseURL成正式HTTPS域名,并把不校验合法域名的开关关掉。后端如果用Flask直跑,本地环境不要死磕证书,直接用这台开发机做内网穿透是最省事的做法。
5.4 现象:管理员改密后,用户还连着旧密码的WiFi
后端管理员把某个热点的WiFi密码改掉,但已有用户仍然连着旧网络,日志里显示该用户用的是旧密码建立连接的。新用户扫同一个码也连不上,提示密码错误。
原因:WiFi密码修改后,已经建立会话的设备在断开前仍然保持连接,这是网络层的行为,小程序无法主动踢掉已连接设备。新用户连不上则大概率是前端缓存了旧密码,小程序端把password存在了storage里,缓存时间又设得特别长。
解决:后端连接记录表里加expire_at,管理员改密后把该热点未过期的记录批量置为过期。前端把缓存的过期时间缩短到5分钟,同时在后端返回的hotspot信息里带一个config_version字段,Redis或MySQL里记录当前版本号,前端每次扫码时先比较版本号,不一致就清掉本地缓存重新请求。更重要的是,改密后让管理员在后台把这台设备的“连接有效期”置零,引导用户在系统WiFi设置里忘记该网络重连。
5.5 现象:小程序提审被拒,提示类目与功能不符
提交审核后,微信反馈“小程序涉及收集用户隐私信息未声明”或“功能涉及诱导打开系统设置”。很多共享类小程序第一次提审都会卡在这里。
原因:小程序读取WiFi信息、申请定位权限、复制剪贴板都涉及用户隐私接口,必须在mp后台的“用户隐私保护指引”中声明收集位置信息、剪切板信息。审核员看到你在代码里调用了相关API,但指引里没有对应声明,就会驳回。
解决:在微信小程序管理后台的用户隐私保护指引里,勾选“位置信息”“剪切板信息”“设备信息”等实际用到的类别,并说明用途是“用于连接门店共享WiFi”。代码里尽量把定位权限的调用时机后置,用户点击扫描WiFi时才申请,而不是启动小程序就要。审核页面不要出现“强制关注公众号才能连WiFi”之类的逻辑,一旦被判定为诱导关注,基本没有申诉空间。
6. 上线后的自检顺序:从管理员改密到设备强制下线
小程序跑通只是第一步,真正值得投入的是把“共享”这件事做成可持续运营。上线前我习惯按三个顺序自检:第一步是管理员操作闭环。管理后台至少要有三个入口:修改热点密码、查看连接记录、强制下线指定设备。强制下线不要只改数据库,要同步更新热点表里的config_version,让前端缓存自动失效。这个动作做完后,用一台旧手机连上WiFi,再去后台点下线,验证这台手机是不是在短时间内被系统踢掉。
第二步是弱网和异常场景验证。把手机的微信小程序杀掉,重新扫同一个码,确认能正常回到热点页而不是卡在启动加载页。后端接口在弱网时返回超时,前端要能显示“网络不给力”而不是白屏。真实场景里,客人站在店里角落扫码时信号很差,这一关过不了,其他功能再好也会被差评。
第三步才是正式发布。把开发者工具里的不校验合法域名开关关掉,重新编译上传,走一遍完整的审核提交流程。审核通过后,再真机扫码跑一次全流程,确认线上版本和联调版本行为一致。
这套独立版源码做到这个程度,已经不只是给客人连WiFi的工具。连接记录里能看到每天的访客高峰时段,每个门店热点了多少人,密码改了多少次。这些数据从平台版里拿不到,却是独立部署最核心的回报。我自己做过的共享类项目里,最常被问的一句话是“为什么不能自动连”,答案是微信就没开放这个能力,与其在系统权限上绕来绕去,不如把引导流程和错误提示写清楚,客人三步内能连上才是真的体验。希望帮到你,少走点系统适配的弯路。
本文还有配套的精品资源,点击获取