告别域名过期焦虑:基于 Go + Wails 3 开发“豆子域名管家”,实现批量监测与企微钉钉预警

我以前曾介绍过,我在微信小程序实现了域名证书监控功能。但是运行一段时间后,发现用的人极少,我在想是否是因为域名以及Webhook隐私的问题。还有就是域名数量太少? 我就想开发一个客户端工具,前端时间使用wails3开发了内网穿透客户端,正好技术可以复用。经过数月的规划和开发测试,豆子域名管家终于可以使用了。这是一款完全本地运行、支持批量导入管理以及可以企微和钉钉通知的域名证书检测工具。旨在解决域名过期监控难题。 特性 传统方式 豆子域名管家 运行方式 依赖云端服务 纯本地运行,数据不出本地 批量处理 手动逐个查询 一键导入,批量删除 通知渠道 单一(邮件/短信) 钉钉+企微双通道,支持Markdown格式 自定义提醒 固定时间提醒 可配置通知时间,提前预警天数 系统集成 无 系统托盘常驻,后台静默运行 隐私安全 数据上传第三方 域名数据和配置数据存本地 技术架构亮点: 工具基于Wails3框架构建,采用Vue3+NaiveUI前端技术栈,确保界面简洁美观,交互流畅,支持暗黑/浅色两种主题。 本地证书检测引擎,直接调用系统网络库进行TLS检测,无需依赖外部API。 多线程并发处理,使用go的特性批量进行域名检测。 跨平台兼容:支持Windows、macOS、Linux系统。 配置持久化,所有配置本地存储,重启后自动恢复。 支持域名证书检测,域名到期时间检测。 软件运行界面预览: 控制面板 配置页面 基本操作指南: 1,导入域名我们需要把要监控的域名按行录入到txt文档中,可以在软件配置界面下载示例模板,当准备完成后,选择刚才的文件,然后验证。没有问题后,点击确认导入即可。 2,配置机器人目前工具支持钉钉机器人和企业微信机器人,支持Markdown格式消息,可以查点击推送预览效果按钮查看推送效果。当输入机器人配置后,可以验证测试,当收到消息后说明配置成功,点击保存即可。可以同时配置企微和钉钉。这样会同时推送两份通知。 3,配置推送通知策略目前工具扫描调度间隔固定24小时,可以配置通知时间,和告警天数间隔。在通知时间,系统会将当天扫描的结果报表推送到机器人。如果用户更新某些域名证书后,可以在监控面板进行手动刷新。想查看效果,可以把通知时间设置为当前时间加几分钟,当到达时间后,将推送报表。确认无误后,可以调整为真正的推送时间。 4,系统托盘运行当完成域名导入和机器人以及通知策略配置后,可以关闭窗口,工具将自动缩放到系统托盘。如果需要退出,需要右键系统托盘退出。注意,如果退出工具,将不能监控域名,因为这是一个本地工具。所以需要工具长期运行。 5,监控面板监控面板的仪表板显示你的域名配置项统计信息,表格显示监视的域名列表。域名可以搜索,刷新以及删除。域名按照过期、告警、正常顺序排序。请查看最前面域名并及时处理。 工具按照自己的真实需求开发,在发布后,比小程序版本要热闹一些,很多人下载尝试。有的用户尝试之后还给了反馈。 如果你需要尝试,可以通过下方下载链接。 https://91demo.top/b011 如果您有任何问题和建议,欢迎反馈和交流。

2026-01-28 · 1 min · Eagle

基于 Dufs + Mole-go (FRP) 快速搭建高效的内网穿透开发演示环境

最近开发完 Mole-go,想给它做个网站用来展示和下载。但我这个后端糙汉子,样式真搞不定,求助 AI 调了半天还是差点意思。最头疼的是,手机端调试得一遍遍输 IP,给朋友演示也得发一串 IP 端口,太不专业了!于是我一顿折腾,搞出了这套方案…… 为了解决这些痛点,我摸索出了一套“黄金组合”:Dufs + Mole-go + FRP + Caddy。这套方案打通了从本地到公网域名的全链路,实现了自动 HTTPS、域名访问以及极致的访问体验。 第一步:构建本地内容基石(Dufs) 一切的起点是本地文件服务。我选择使用 Dufs 作为静态文件服务器。它极其轻量,支持上传、搜索、打包下载甚至 WebDAV,是我演示 Web 应用或分发安装包的首选。 通过下面简单的命令,就能在本地 5000 端口启动了静态文件及Web服务。虽然此时它还被“困”在局域网内,但它为后续的展示提供了稳固的基础。 dufs.exe --render-index 我们这个时候将HTML文件放在启动命令时所在文件夹下就可以了。 第二步:突破局域网束缚(FRP 与 Mole-go) 为了让公网流量能顺利精准触达内网,我采用了经典的 FRP 方案,但在客户端层面,我使用了自己开发的 Mole-go。它需要以下几部分配合,注意,像FRP Server部署一次就可以一直使用了。 服务端 (FRP Server):部署在具备公网 IP 的云服务器上,充当流量中转站。这个服务器配置可以很低,因为网站服务在本地电脑,即使本地有数据库,也是消耗的本地主机资源,如果仅作为演示,带宽1MB就可以,这样在公网就可以访问了,非常方便远程调试。 客户端 (Mole-go):这是我为 FRP 打造的桌面管理客户端。它封装了 frpc 核心,不仅提供了直观的 UI,还通过系统托盘设计彻底解决了“关闭窗口即断连”的痛点。 使用 Mole-go,我可以将本地 5000 端口通过加密隧道安全地映射到云端。它出色的资源管理和连接稳定性,确保了演示过程中即便网络波动,链接依然稳固如初。它的下载地址:https://91demo.top/tools/,中文名称是豆子内网管家。 第三步:优雅的网关入口(Caddy) 即便流量已到达公网,我也不希望朋友们通过 http://IP:端口 这种生硬的方式访问。我追求的是“域名+HTTPS”的专业感,这不仅是为了美观,更是为了开发环境需求,比如公众号开发,小程序后端服务等必须 HTTPS 环境。仅需配置一次就可以一直使用了。 我选择了 Caddy 担任“守门人”。Caddy 的魅力在于其近乎零配置的 自动 HTTPS 功能。我看中了它的简单方便,它非常符合我的场景。在 Caddyfile 中,我只需写下: example.com { reverse_proxy localhost:7000 # 指向 FRP 映射出的本地端口 } 仅需这一行配置,Caddy 就会自动搞定 SSL 证书的申请与续签。当访问者输入域名时,映入眼帘的是受信任的绿色小锁头,所有的复杂端口逻辑都被完美隐藏。当然,如果你需要高性能,或者服务器已经有了Nginx,你也可以直接使用它。 ...

2026-01-15 · 1 min · Eagle

实战笔记:实现一个语音验证码远程呼叫版本

在实现了主动使用VOIP客户端拨打8000号码播报语音验证码的功能后,我发现了一个最大的缺点,就是这需要用户主动去操作。这对于想使用API集成的第三方应用来说根本无法实现。 在思考之后,我决定实现一个可以调用API就呼叫VOIP客户端的版本,当呼叫用户并且用户接通后,自动播报语音验证码的功能。当实现这个功能后,它的好处是显而易见的。比如,可以集成到嵌入式,集成到第三方网站,直接调用API就可以呼叫出去。 那么该如何实现它呢? 一、 系统原理 传统的拨号方案(Dialplan)是静态的,而 ARI 允许我们动态控制。整个“API 触发呼叫并播报”的流程如下: 触发阶段:第三方系统通过 API 向 Go 服务发送呼叫请求(包含目标 ID 和验证码)。 呼叫发起(Originate):Go 服务调用 Asterisk ARI 的 /channels 接口。此时 Asterisk 会尝试向 PJSIP 终端(或通过中继向手机)发起呼叫。 接通监听(Stasis Start):一旦用户接起电话,该通道会被移交给一个名为 Stasis 的应用。此时 Go 服务会收到一个“通道已接通”的 WebSocket 事件。 语音合成与播放:Go 服务识别到接通后,调用播报指令(可以播放预录音文件,或对接 TTS 引擎生成的语音流)。 挂断处理:播报完毕后,服务发送挂断指令,释放资源。 二、系统架构 [第三方API] --> [Go 后端服务] --(REST API)--> [Asterisk ARI] | | (WebSocket) (PJSIP/IMS) | | [接通状态回调] <--- [用户终端接听] 三、 核心代码实现 (Golang) 这里使用了 GitHub 上的 go-ari 库。 1. 初始化 ARI 客户端 import ( "github.com/v5" "github.com/v5/client/native" ) // 连接到 Asterisk ARI cl, err := native.Connect(&native.Options{ Application: "voice-verify", // 必须与 asterisk.conf 配置一致 Username: "admin", Password: "password", URL: "http://localhost:8088/ari", }) 2. 实现呼叫并播报语音验证码 这是核心逻辑:接收参数 -> 发起呼叫 -> 监听接通 -> 播放语音。 ...

2026-01-13 · 3 min · Eagle

深度实战:基于 Wails v3 与 Go 打造跨平台 FRP 桌面客户端 Mole-go 的技术架构与原理

一、 缘起:为什么需要 mole-go? 在开发微信公众号、调试支付接口、以及演示本地开发网站时,或由于服务器资源限制需要在本地部署服务时,frp 是不可或缺的内网穿透神器。然而,原生的 frpc 存在几个显著的痛点: 运行隐形性差:必须开启命令行窗口,一旦误关服务即中断。 配置门槛高:新手难以记忆复杂的 .toml 参数。 为了解决这些问题,我开发了 mole-go。它是一个轻量级、跨平台的桌面客户端,旨在实现 frp 的配置、启动与监控一体化。 在选择使用哪个语言,哪个库开发时,我尝试了多个,分别为go fyne,rust tauri,rust iced,go wails3。 放弃Go fyne是因为frp日志列表显示达不到我的要求,放弃rust tauri是因为frp二进制启动和关闭我无法实现,放弃rust iced是因为只开发了部分,界面和进度无法我无法掌控,并且此时我了解到了wails v3版本。我最终选择 Wails v3 则是看中了其原生渲染、系统托盘支持、Go 强力后端以及极小的打包体积。最主要的原因是我熟悉Go,可以用它实现功能。 二、 核心架构:Go + Wails v3 的化学反应 mole-go 采用了经典的“UI-Backend-Service”三层架构: Wails UI:负责前端展示,通过事件驱动(Event-Driven)与后端交互。 Go Backend:核心大脑,负责业务逻辑、进程管理与系统级 API 调用。 frpc 二进制:底层服务,通过 Go 的 embed 特性内嵌到二进制文件中。 三、 关键实现细节:从命令行到图形化的进化 前端:从“面条代码”到模块化数据驱动 早期版本中,我直接采用 window.startFrp,window.stopFrp这样的写法,导致代码碎片化严重,以及管理app运行状态不方便。在 mole-go 的正式版中,我将其重构为数据驱动模式,类似Vue,由数据驱动界面: 模块化封装:定义全局 window.App 对象,将数据状态与行为(Methods)统一封装,使代码结构清晰。 动态 UI 组件:针对 HTTP、TCP、UDP 等不同代理模型,不再机械地堆砌 HTML 片段,而是通过逻辑判断实现“按需渲染”,大大精简了 DOM 结构。 后端:全局实例与事件机制 为了保证服务层(Service)能随时与 UI 通信,我设计了一个全局 App 实例,这样可以方便得调用和管理。 状态约定:前后端约定一套状态码,通过 Wails v3 的 Events 机制,后端可以主动向前端推送 frpc 的运行状态、日志等信息。 独立服务层:将 frp 相关逻辑抽离到专门的文件中,通过 Wails 的 Binding 暴露给前端,保持代码的解耦。 系统深度集成 系统托盘(System Tray):利用 Wails v3 原生的托盘支持,实现了“关闭即隐藏”逻辑。 外部链接调用:使用 wails3自带的 Browser.OpenURL 方法,确保点击文档链接时能正确唤起系统浏览器。 可参考项目源码。 ...

2026-01-10 · 1 min · Eagle

FRP 图形化管理新方案:基于 Wails 3 的 Mole-go 桌面客户端部署指南

记录自研 FRP 管理工具 Mole-go 的公版部署与使用过程,探讨如何通过 Go + Wails 3 构建极致简单的内网穿透图形化管理体验。 在介绍部署高性能的内网穿透开发演示环境时,虽然也提到了部署配置,但是不太详细。这次本文将带你详细的快速完成基于 Mole 的内网穿透服务部署,包含服务端 (frps) 与桌面客户端 (Mole/frpc) 的配置与排查要点。 📖 核心概念 首先,我们了解几个关键概念,只有了解之后我们才能进行扩展,应用到更多的场景中。 什么是 FRP? FRP (Fast Reverse Proxy) 是一款高性能的反向代理/内网穿透工具,它通过在公网服务器和内网客户端之间建立隧道,使外网可以访问内网服务(如 NAS、树莓派、开发环境等)。 什么是 Mole? Mole 是一款基于 wails3 开发的 FRP 桌面客户端,提供图形化管理界面,简化 frpc 的配置与使用: 告别繁琐命令行 支持系统托盘常驻,防止误关窗口导致中断 支持 HTTP/HTTPS、TCP、UDP 等多种协议 🛠️ 部署前准备 必须有一台拥有公网 IP 的低配云服务器(例如1核1G内存的阿里云、腾讯云等),如果你需要提供视频等服务,那么带宽要高,服务器配置视情况进行调高。 FRP 服务端(frps)建议使用 FRP Releases 的 v0.65.0 或更高版本,可以从https://91demo.top/tools/下载我已经打包好各平台的服务端。 Mole 桌面客户端安装包,可以从https://91demo.top/tools/直接下载,或者从Github下载源码littletow/mole-go编译。 需要具备基本网络与防火墙管理知识。 1. 服务端配置(frps) 在你拥有公网IP的云服务器上部署frps服务端: 下载并解压 FRP 服务端(以 Linux 为例) 访问 FRP 的 Releases 页面下载对应版本并解压(示例为 v0.65.0 或更高)。 ...

2026-01-07 · 2 min · Eagle

Wails 3 初体验及在FRP管理客户端中的应用

为什么选择了Wails 3 ? Wails 3 最大的改变在于它不再强绑定于某个特定的前端框架,且引入了多窗口支持和更轻量级的 Runtime。它允许你在不启动主窗口的情况下运行后端服务,这正是我们实现“系统托盘”和“后台演示服务”的基础。 在 v2 中,我们习惯于自动生成的 wailsjs 文件夹。但在 v3 中,这一逻辑被进一步标准化。 当你运行开发指令时,Wails 会扫描你的 Go 结构体方法,并将其映射为前端可以调用的 JavaScript 函数。这个过程在 v3 中被称为 Generate 过程。 Wails 3 通信灵魂 我们在前端调用在后端定义的 HandleConnect 返回自定义结构体,这在 Wails 3 中是前后端通信的灵魂。 在 Wails 3 开发中,最核心的动作就是:后端做功,前端表现。 当你调用 MoleService.HandleConnect() 时,Go 后端会产生一个结果。在本项目中,我们需要同时返回一个 Code(状态码)和一个 Content(数据内容)。 为了实现这一点,我们定义了一个结构体: type Response struct { Code int; Content string } 虽然 Go 内部使用的是结构体,但前端 JavaScript 只能读懂 JSON 对象。Wails 3 内部会自动帮你完成这个“翻译”过程。 但是,如果你想让前端看到的字段名是小写的(例如 res.code 而不是 res.Code),你必须在 Go 结构体定义时加上“注解”。 type Response struct { Code int `json:"code"` Content string `json:"content"` } 记住,所有通过 bindings 调用的 Go 方法,在前端返回的都是一个 Promise 对象。这意味着你必须使用 await 或者 .then() 来接收数据,否则你拿到的将是一个永不开启的“盲盒”。 ...

2025-11-01 · 4 min · Eagle

Markdown 进化论:从小程序文章列表到游戏关卡到时间轴作品集再到项目模块集市

Markdown越来越玩的纯熟,在小程序架构稳定之后,剩下的就是Markdown内容的编辑,我已经不满足于基础的标题和段落,我还会根据适当的场景使用。我必须把常用的Markdown介绍一下,感觉其设计真是奇妙,比如在一行中用三个以上的星号、减号、底线来建立一个分割线,行内不能有其它东西。也可以在星号或减号中间插入空格。这样就是一条分割线。当你完全掌握之后,就会发现我们还可以进行扩展,比如我后面的跳转公众号文章,跳转小程序。 我们先来快速浏览一下除了标题和段落之外不常用但重要的Markdown语法。不感兴趣可以跳过。 *斜体文本* _斜体文本_ **粗体文本** __粗体文本__ ***粗斜体文本*** ___粗斜体文本___ # 分割线 *** * * * ***** - - - --------- ~~删除线~~ <u>下划线文本</u> [^脚注,要注明的文本] # 无序列表 * 第一项 + 第二项 - 第三项 # 有序列表 1. 第一项 2. 第二项 3. 第三项 # 列表嵌套需要在子列表中的选项签名添加两个或四个空格即可。 1. 第一项: - 第一项嵌套的第一个元素 - 第一项嵌套的第二个元素 2. 第二项: - 第二项嵌套的第一个元素 - 第二项嵌套的第二个元素 # 区块 > 最外层 > > 第一层嵌套 > > > 第二层嵌套 # 列表中使用区块,如果列表中要使用区块,请在>前添加四个空格即可。 * 第一项 > 菜鸟教程 > 学的不仅是技术更是梦想 * 第二项 # 链接 [链接名称](链接地址) <链接地址> # 图片显示 ![alt 属性文本](图片地址) ![alt 属性文本](图片地址 "可选标题") ![alt 属性文本](图片地址居中#pic_center) ![alt 属性文本](图片地址宽和高 =400x200) # 只设置宽,会按比例进行缩放 ![alt 属性文本](图片地址 =400x) # 使用 html <img>标签。 <img src="https://xxx.com/x.png" width="50%"> # 制作表格使用|来分割不同的单元格,可以使用-来分割表头和其它行。 | 表头 | 表头 | | ---- | ---- | | 单元格 | 单元格 | | 单元格 | 单元格 | # 设置表格的对齐方式使用如下符号: -: 设置内容和标题栏居右对齐。 :- 设置内容和标题栏居左对齐。 :-: 设置内容和标题栏居中对齐。 # Markdown 使用了很多特殊符号来表示特定的意义,如果需要显示特定的符号则需要使用转义字符,Markdown 使用反斜杠转义特殊字符: **文本加粗** \*\* 正常显示星号 \*\* 使用 KaTex 或 MathJax 来渲染数学表达式。 Mardown语法扩展 当然我还扩展了一些内容,比如链接,我在小程序中支持跳转到公众号文章,以及跳转到小程序。具体的实现就是通过前缀。 ...

2025-09-03 · 1 min · Eagle

万物皆可 Markdown:小程序动态内容渲染通用架构设计

在学习了微信小程序之后,我使用wxml页面可以做出唐诗的内容了。结合wxss,我可以定义好看的布局。在小程序需要备案时,因为唐诗需要资质,而我个人无法满足这些条件,所以我需要开发新的项目。恰好最近在为我的技术笔记发愁。我希望有一个可以展示我笔记内容的工具。 我的最初想法非常简单: 1,可以更新文章内容而不需要升级小程序。 2,需要在小程序端进行展示,并且需要美观。 3,能够根据标题或者关键字进行搜索文章内容。 经过一番研究后,我决定小程序使用原生开发,界面 UI 使用 WeUI,内容渲染使用Markdown。 小程序页面架构是列表+详情。列表作为首页,首页页面非常简单,一个标题和描述,用来解释小程序干什么?一个搜索框用来搜索内容,三个快捷按钮用于快速查找内容。 这个也是为了小程序审核,避免成为资讯类主题。 在完成之后,我发现这套架构可以通用,用来渲染动态内容。像列表页面以及WEUI使用,熟悉前端的非常容易理解实现。下面是一些核心介绍,主要介绍Markdown的渲染实现。 在小程序中,无法直接展示 Markdown 数据,我们需要将 Markdown 转换为 WXML,网上的大神很多,有多款 Markdown 转 WXML 库,我使用的是 TOWXML 库。我使用Markdown主要是因为更新内容不用升级小程序,这是动态渲染的最大优点。当然它还有其它一些优势,比如容易编辑,支持数学公式,布局美观等。 下面是Markdown具体实现,不感兴趣可以跳过。可以看技术下面的架构思考。 1,按照 towxml 库的说明文档,我们将编译后的 towxml 复制到我们的项目中,然后在 app.js 文件中引入这个组件。 App({ towxml: require('./towxml/index'), 2,按照首页的步骤,我们添加文章详情页面。 "pages/article/index" 在 index.wxml 文件中,我们添加页面布局,这个很简单,显示渲染后的内容即可。 <!--pages/article/index.wxml--> <view class="page"> <!--使用towxml--> <towxml nodes="{{article}}" /> </view> 3,注意,我们还需要在 index.json 中添加组件引入。 { "usingComponents": { "towxml": "../../towxml/towxml" } } 4,在 js 文件中,我们需要调用 Markdown 数据。 onLoad(options) { const _ts = this; wx.showLoading({ title: '加载中', }) httpGet('/artd', { uuid: options.guid, }).then((res) => { const result = res.data; if (result.code == 1) { let content = result.data; let obj = app.towxml(content, 'markdown', { theme: 'light', events: { tap: (e) => { console.log('tap', e); } } }); _ts.setData({ article: obj, }); wx.hideLoading({ success: (res) => { }, }) } else { wx.hideLoading() } }).catch((err) => { console.log(err); wx.hideLoading() wx.showToast({ title: '网络异常请重试', }) }) }, 我们在 onLoad 中加载函数,这样页面启动就会自动网络请求,然后渲染 Markdown 数据。在请求数据时,我们注意到 guid,这个参数是上一个页面即首页,点击文章列表项带过来的数据。 你也可以使用其它参数,它能唯一代表从服务器拉取哪篇文章的Markdown内容。 ...

2025-06-03 · 2 min · Eagle

构建自定义验证码登录系统及研究语音验证码实战

当我使用小程序码登录网站时,我发现了一个问题,在手机端不方便登录。我们都知道手机号验证码可以登录网站,但是我没有资源去实现手机号验证码功能,我使用一个变通的方案,在手机端不使用手机号验证码也能登录。 小程序实现验证码登录 它的核心还是鉴权,我在小程序端制作了一个获取验证码界面,它可以生成模拟编号和验证码。当用户点击获取验证码时,会向后台请求返回编号和验证码,后台这个时候会记录为哪个用户openid生成了哪个验证码。那为什么不单单使用验证码呢?还要再添加一个编号,不麻烦吗?因为单单使用验证码会发生撞车的可能。要知道,验证码一般4位或者6位,很容易被暴力攻击的。 当用户在网站端输入编号和验证码时,后端会校验是否存在这对编号和验证码,如果校验正确,将取出openid并绑定到sessionID上,然后返回给前端,存入cookie中。可以看看前面的文章,扫描二维码登录,同样的道理。 除了在小程序生成验证码外,我们还可以在公众号中发送消息获取验证码。它其实也是使用了微信的账号体系。 公众号实现验证码登录 要知道公众号不仅是内容分发平台,更是一个强大的身份认证中间件。我们使用发送消息来获取验证码信息。 1. 握手与回调 (Webhook) 当你在公众号消息发送验证码三个字时,微信会推送事件到你的服务器。我使用go对接了微信公众号消息。 要在 Go 中接收微信消息,你必须先在微信公众平台配置一个 服务器地址 (URL)。** 微信会向你的 URL 发送一个 GET 请求,包含签名、随机数等。你必须按照规定的算法计算并返回正确的 echostr,这被称为“服务器验证”。 验证通过后,每当用户发送消息,微信服务器就会以 POST 方式将消息体推送给你。 2. 解析 XML 数据 与现代 API 不同,微信公众号的推送采用的是 XML 格式。Go 的标准库 encoding/xml 提供了强大的解析能力: type WxMsg struct { ToUserName string `xml:"ToUserName"` FromUserName string `xml:"FromUserName"` // 这就是用户的 OpenID Content string `xml:"Content"` // 用户发来的文字 } 3. 验证码生存逻辑 当用户发送“验证码”关键字时,我们的 Go 后端会生成一个 4-6 位的随机数和一个编号。并将消息中的 OpenID 与 编号和随机数 存入 Redis,并设置 TTL(如 5 分钟)。然后通过 XML 响应将验证码发回给用户。 4. 身份绑定,完成登录 我们在Web HTML页面提供了登录表单,提供编号和验证码输入框。用户输入编号和验证码后提交到后端,后端从 Redis 中根据编号和验证码反查 OpenID,若存在且有效,则代表身份验证成功,返回包含身份的TOKEN。 ...

2025-03-21 · 1 min · Eagle

基于 Go + 小程序实现网页端“扫码登录”实战

不想使用账号和密码登录,害怕被攻击,也不想做注册等功能;不想使用手机号和验证码登录,没有钱也没有资质去做这些。我想到了二维码,通过二维码进行登录。这里有一个核心的问题还是如何鉴权? 在了解小程序后,我就决定使用小程序扫描二维码登录,使用小程序自带的微信账户体系完成鉴权。 我们要知道,二维码(QR Code)是连接物理世界与数字世界的“虫洞”。在登录系统中,它承载着一个临时的身份信标。 在实现这个Web登录功能的过程中,我们需要: 1. 生成有效二维码 这个二维码需要显示在网页上,供用户扫码登录,登录二维码通常包含一个加密的 URL 或一个唯一的 UUID(通用唯一识别码)。 这个唯一识别码需要具备的特性: 唯一性: 每一对扫描动作都必须对应一个独一无二的 ID。 时效性: 二维码必须配合 Redis 设置过期时间(如 2 分钟),逾期自动失效。 在本项目中,我们将用户的微信 OpenID 作为 Key,生成的验证码作为 Value,使用Redis进行存储: // 存储验证码,并设置 5 分钟过期 err := rdb.Set(ctx, "user:123:code", "8888", 5*time.Minute).Err() // 读取验证码 val, err := rdb.Get(ctx, "user:123:code").Result() 当有了二维码内容后,我们需要工具来生成二维码,在 Go 生态中,我们使用 skip2/go-qrcode 库来完成像素的绘制: // 生成二维码字节数组 var png []byte png, _ = qrcode.Encode("91demo.top"+sessionID, qrcode.Medium, 256) 为了防止用户伪造扫码请求,二维码里的内容通常是加密的或者是不可预测的长随机数(UUID)。只有真实存在的 ID 才能通过后端的 Redis 校验。 2. 传输二维码 当在服务端生成二维码后,还需要传递给浏览器,让浏览器进行显示,用户才可以扫码。这里有两个方法:1,生成图片文件,然后浏览器下载后显示。2,将图片内容转为Base64字符串传递给浏览器。这里我们选择了后者,我们不希望在用户硬盘上产生大量的临时 .png 文件。 在Go中可以这样操作: // 转换为前端可直接识别的 Data URL base64Img := "data:image/png;base64," + base64.StdEncoding.EncodeToString(png) 为了让前端不至于崩溃,后端必须返回统一的格式。 func HandleLogin(c *gin.Context) { // 逻辑处理... c.JSON(200, gin.H{ "code": 1, "content": base64Img, }) } 在前端,我们不再需要引入沉重的第三方库来做简单的请求。浏览器原生提供的 Fetch API 简洁且基于 Promise。 ...

2025-03-15 · 2 min · Eagle