首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >SpringBoot微信公众号实现发送验证码登录功能(全流程详解+源码)

SpringBoot微信公众号实现发送验证码登录功能(全流程详解+源码)

原创
作者头像
天天困
修改2026-01-15 12:21:23
修改2026-01-15 12:21:23
3400
举报
文章被收录于专栏:技术方案技术方案

前言:为什么选择“扫码-验证码”登录?

在 Web 应用开发中,用户登录是必不可少的一环。传统的账号密码登录往往让用户感到繁琐:忘记密码、重复注册、输入错误... 而微信扫码登录虽然便捷,但个人订阅号并不支持网页授权(OAuth2.0)

这就陷入了一个死胡同吗?当然不是!

我们完全可以利用微信公众号的基础消息接口,通过“曲线救国”的方式实现一种安全、低成本且用户体验极佳的登录方案:用户扫码关注 -> 回复关键词 -> 获取验证码 -> 网页输入登录

本文将带你从零开始,使用 Spring Boot + Hutool 打造一套完整的验证码登录系统,并附带内网穿透工具的使用指南。


一、 核心流程设计

这种登录方式的本质,是打通“微信用户”与“网页端”的信息壁垒。

1. 业务时序图 (Mermaid)


二、接入前准备

1. 公众号类型选择

微信公众号分为订阅号、服务号和企业号,不同账号类型的功能权限差异较大:

账号类型

认证要求

接口权限

适用场景

订阅号

个人/企业

基础接口

资讯推送

服务号

企业认证

全部接口

服务交互

测试号

无需认证

全部接口

开发测试

提示:为了展示最真实的开发流程,本文将使用真实的微信公众号进行实战演示,带你体验完整的接入过程。

2. 获取接口配置信息(重要变动)

在开始开发前,我们需要在微信后台配置服务器地址(URL)和令牌(Token)。

⚠️ 2025年12月1日 新版入口变动说明

以前我们都是直接在 微信公众平台 (mp.weixin.qq.com) 的“基本配置”里进行设置。

但从 2025年12月1日 起,微信官方将“开发者工具”“接口权限配置”相关功能迁移到了全新的 微信开发者平台

具体操作流程:

  1. 登录公众号:首先访问 微信公众平台 (mp.weixin.qq.com),登录您的公众号。
  2. 跳转新平台:在左侧菜单栏找到“开发”或“设置”相关选项,点击后会提示您跳转至新版地址,或者您也可以直接访问 微信开发者平台 (developers.weixin.qq.com/platform)
  3. 扫码登录:使用绑定的管理员微信号扫码登录新平台。
  4. 填写配置:找到 “服务器配置”“接口配置” 模块,填写以下信息:

URL: 填写您的公网访问地址(需配合下一步的内网穿透),例如 http://你的域名/wx/callback

Token: 自定义一个字符串(如 mySecretToken),必须与 Java 代码保持一致。

注意:此时先不要点击“提交”,等 Java 代码写好并启动后再提交。

3. 环境准备与内网穿透

微信服务器需要访问你本地的开发电脑,所以需要“内网穿透”。

  • 环境:JDK 17、IDEA、Natapp (或 Cpolar)
  • 工具Natapp

配置 Natapp:

  1. 注册与购买:访问 Natapp官网,注册后购买一个“免费隧道”。
  2. 配置隧道:在后台管理页面点击“配置”,将 本地端口 改为 8080(必须与 Spring Boot 端口一致)。
  3. 下载客户端:下载对应的客户端文件(如 natapp.exe)。
  1. 启动隧道
  • 打开命令行工具(CMD 或 PowerShell),进入 natapp.exe 所在的目录。
  • 运行以下命令启动(请将 YOUR_AUTHTOKEN 替换为你后台显示的 authtoken):

natapp -authtoken=YOUR_AUTHTOKEN

  1. 获取地址:看到如下界面说明成功,复制 Forwarding 后的公网地址(如 http://xxxx.natappfree.cc)。

三、 完成服务器接入

1. 平台配置

微信公众号的对接,需要做一个 Spring Boot 服务,提供同名接口的不同请求方式:

  • GET 请求:用于验证签名(微信服务器和你服务器的“握手”)。
  • POST 请求:用于接收用户发送的消息。

配置步骤:

回到微信测试号管理后台,找到 “接口配置信息”

  • URL: 填写 Natapp 生成的公网地址 + 接口路径,例如 http://xxxx.natappfree.cc/wx/callback
  • Token: 自定义一个字符串(如 mySecretToken),必须与 Java 代码保持一致

注意:此时先不要点击“提交”,等 Java 代码写好并启动后再提交。

2. 典型消息处理流程


三、 代码实战

1. 依赖引入 (Maven)

除了 Spring Web,我们引入两个神器:

  • commons-codec: 也就是 Apache 的加密库,用于 SHA1 签名验证(不用自己手写算法了)。
  • hutool-all: 国产 Java 工具包之光,用来生成随机数、管理带过期的缓存、解析 XML 等。
代码语言:xml
复制
<dependencies>
    <!-- 简化代码 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <!-- XML 解析 -->
    <dependency>
        <groupId>org.dom4j</groupId>
        <artifactId>dom4j</artifactId>
        <version>2.1.3</version>
    </dependency>
    <!-- 签名加密 -->
    <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
    </dependency>
    <!-- Hutool 工具包 -->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.16</version>
    </dependency>
</dependencies>

2. 配置文件 (application.properties)

将配置外置,方便管理。

代码语言:properties
复制
server.port=8080
# 微信公众号Token,请确保与微信后台配置一致
wechat.token=mySecretToken

3. 核心控制器:CallBackController

这是与微信服务器“对话”的唯一入口。

注意: 微信后台配置 URL 时,如果填的是 http://域名/wx/callback,那么我们的 Controller 就要监听这个路径。

代码语言:java
复制
@Slf4j
@RestController
@RequestMapping("/wx")
public class CallBackController {

    @Value("${wechat.token}")
    private String token; // 读取配置文件

    // 使用 Hutool 的 TimedCache,自动管理过期,无需手动清理!
    // Key: 验证码, Value: 用户OpenID
    public static final TimedCache<String, String> LOGIN_CACHE = CacheUtil.newTimedCache(300 * 1000); // 5分钟

    static {
        LOGIN_CACHE.schedulePrune(1000); // 每秒检查一次过期
    }

    /**
     * 1. 服务器验证接口 (GET)
     * 微信后台点击“提交”时调用,用于确认服务器身份。
     */
    @GetMapping("/callback")
    public String verify(@RequestParam(name = "signature", required = false) String signature,
                         @RequestParam(name = "timestamp", required = false) String timestamp,
                         @RequestParam(name = "nonce", required = false) String nonce,
                         @RequestParam(name = "echostr", required = false) String echostr) {
        
        // 1. 参数校验(防御性编程)
        if (signature == null || timestamp == null || nonce == null || echostr == null) {
            return "fail";
        }

        // 2. 签名校验 (安全核心)
        // 将 token、timestamp、nonce 字典排序并 SHA1 加密
        String sha1 = SHA1.getSHA1(token, timestamp, nonce); 
        
        // 3. 比对成功,原样返回 echostr
        if (sha1 != null && sha1.equals(signature)) {
            return echostr;
        }
        return "fail";
    }

    /**
     * 2. 消息接收接口 (POST)
     * 用户发送消息时,微信会将 XML 推送到这里。
     */
    @PostMapping(value = "/callback", produces = "application/xml;charset=UTF-8")
    public String handleMessage(@RequestBody String requestBody,
                                @RequestParam("signature") String signature,
                                @RequestParam("timestamp") String timestamp,
                                @RequestParam("nonce") String nonce) {
        
        // 同样需要验签,防止黑客伪造请求
        if (!SHA1.checkSignature(token, timestamp, nonce, signature)) {
            return ""; 
        }

        // 解析 XML
        Map<String, String> msgMap = MessageUtil.parseXml(requestBody);
        String fromUser = msgMap.get("FromUserName"); // 用户 OpenID
        String toUser = msgMap.get("ToUserName");     // 公众号 ID
        String content = msgMap.get("Content");       // 消息内容

        // 业务逻辑:用户回复 "验证码"
        if ("text".equals(msgMap.get("MsgType")) && "验证码".equals(content)) {
            // 生成 6 位纯数字验证码
            String code = RandomUtil.randomNumbers(6);
            
            // 存入缓存
            LOGIN_CACHE.put(code, fromUser);
            
            // 构造回复 XML
            // 注意:回复时,发送人是公众号(toUser),接收人是用户(fromUser)
            return MessageUtil.textMessageToXml(fromUser, toUser, 
                "您的验证码是:" + code + "\n有效期为5分钟。");
        }
        
        return MessageUtil.textMessageToXml(fromUser, toUser, "回复 '验证码' 获取登录验证码。");
    }
}

4. 工具类封装

为了代码整洁,我们把繁琐的逻辑抽离出来。

SHA1 签名工具类 (利用 commons-codec)

代码语言:java
复制
public class SHA1 {
    public static String getSHA1(String token, String timestamp, String nonce) {
        String[] arr = new String[]{token, timestamp, nonce};
        Arrays.sort(arr); // 1. 字典序排序
        StringBuilder sb = new StringBuilder();
        for (String s : arr) sb.append(s);
        return DigestUtils.sha1Hex(sb.toString()); // 2. SHA1 加密 (一行代码搞定!)
    }
    
    // 封装校验逻辑
    public static boolean checkSignature(String token, String timestamp, String nonce, String signature) {
        String sha1 = getSHA1(token, timestamp, nonce);
        return sha1 != null && sha1.equals(signature);
    }
}

MessageUtil XML 处理类

这里不需要复杂的对象映射,直接字符串拼接 XML 是性能最高且最不易出错的方式。

代码语言:java
复制
public static String textMessageToXml(String toUserName, String fromUserName, String content) {
    return "<xml>" +
            "<ToUserName><![CDATA[" + toUserName + "]]></ToUserName>" +
            "<FromUserName><![CDATA[" + fromUserName + "]]></FromUserName>" +
            "<CreateTime>" + System.currentTimeMillis() / 1000 + "</CreateTime>" +
            "<MsgType><![CDATA[text]]></MsgType>" +
            "<Content><![CDATA[" + content + "]]></Content>" +
            "</xml>";
}

四、 前端交互实现

前端页面非常简单,核心逻辑是:轮询 或者 用户手动点击登录。这里演示最简单的“输入验证码登录”模式。

代码语言:html
复制
<!-- 核心代码片段 -->
<div class="login-box">
    <!-- 请替换为你自己的二维码图片 -->
    <img src="你的公众号二维码.jpg" alt="扫码关注">
    <p>关注回复 <b>"验证码"</b> 获取登录码</p>
    
    <input type="text" id="code" placeholder="输入6位验证码">
    <button onclick="doLogin()">登录</button>
</div>

<script>
function doLogin() {
    let code = document.getElementById("code").value;
    // 调用后端接口
    fetch('/login?code=' + code)
        .then(res => res.text())
        .then(data => {
            if(data.includes("成功")) {
                alert("登录成功!OpenID: " + data);
                location.href = "/success.html";
            } else {
                alert(data);
            }
        });
}
</script>

五、 成果展示

经过以上步骤,我们已经完成了一个完整的闭环。让我们来看看最终的效果:


六、 避坑指南 & 最佳实践

  1. Token 一致性:代码里的 wechat.token 必须和微信后台填写的 Token 完全一致。如果不一致,后台提交配置时会提示“Token验证失败”。
  2. 明文模式:对于初学者,微信后台的“消息加解密方式”请务必选择 “明文模式”。如果选了安全模式,你的代码需要引入 AES 解密库,复杂度直线上升。
  3. 收发人反转:这是最容易犯错的地方!
    • 接收消息时:From 是用户,To 是公众号。
    • 回复消息时:必须反过来To 是用户,From 是公众号。如果不反转,用户永远收不到消息(因为你发给公众号自己了)。
  4. Hutool 的 TimedCache:不要用 Map 做缓存!因为 Map 不会自动清理过期数据,运行久了会内存泄漏。Hutool 的 TimedCache 是轻量级场景的最佳选择。

结语

通过这套方案,我们成功绕过了个人订阅号的权限限制,实现了一个体验流畅的扫码登录功能。这不仅展示了 Spring Boot 的灵活性,也体现了 Hutool 等工具库在提升开发效率上的巨大价值。

希望这篇详细的教程能帮助到你,如果有任何问题,欢迎在评论区留言交流!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言:为什么选择“扫码-验证码”登录?
  • 一、 核心流程设计
    • 1. 业务时序图 (Mermaid)
  • 二、接入前准备
    • 1. 公众号类型选择
    • 2. 获取接口配置信息(重要变动)
    • 3. 环境准备与内网穿透
  • 三、 完成服务器接入
    • 1. 平台配置
    • 2. 典型消息处理流程
  • 三、 代码实战
    • 1. 依赖引入 (Maven)
    • 2. 配置文件 (application.properties)
    • 3. 核心控制器:CallBackController
    • 4. 工具类封装
  • 四、 前端交互实现
  • 五、 成果展示
  • 六、 避坑指南 & 最佳实践
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档