我们都知道手机号验证码可以登录网站。这里我也做了类似的功能,方便在手机端不能使用扫码也能登录。

我在小程序端制作了一个获取验证码界面,它可以生成模拟编号和验证码。当用户点击获取验证码时,会返回编号和验证码,它同时绑定用户的小程序身份Openid。

当用户在网站端输入编号和验证码时,后端会校验是否存在这对编号和验证码,如果校验正确,将取出openid并绑定到sessionID上,然后返回给前端,存入cookie中。

除了在小程序生成验证码外,我还添加了在公众号发送消息获取验证码。

1. 公众号获取验证码

公众号不仅是内容分发平台,更是一个强大的身份认证中间件

1. 握手与回调 (Webhook)

要在 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. 验证码生存逻辑 (Redis 联动)

当用户发送“验证码”关键字时,我们的 Go 后端会:
生成一个 4-6 位的随机数。
将 OpenID 与 随机数 存入 Redis,并设置 TTL(如 5 分钟)。
通过 XML 响应将验证码发回给用户。

4. 身份对撞

用户在Web端输入这个验证码。后端从 Redis 中根据验证码反查 OpenID,若存在且有效,则代表身份验证成功。执行登录流程。

语音验证码

我还使用Asterisk模拟实现了语音验证码功能。语音验证码不仅是“声感”的提升,更是安全策略的补充。

1. 语音验证的业务流

  1. 请求: 用户在小程序端点击“获取语音验证码”。
  2. 生成: 后端生成 6 位随机数字,并存入 Redis。
  3. 呼叫: 后端调用ARI的 API,传入用户的PJSIP账号和验证码变量。
  4. 播报: Asterisk开始呼叫用户,用户接通电话后,通过预录制音频读出验证码并进行播放。

2. 并发与异步:不要阻塞

发送语音请求通常需要 1-3 秒才能得到Asterisk的响应。在 Go 中,我们绝不能让主处理函数在那里“傻等”。

通过使用 Goroutine,我们可以瞬间返回“已发起呼叫”的信号给前端,而真实的 API 请求在后台悄悄进行。

// 异步发起语音呼叫
go func(phone string, code string) {
    err := smsService.Call(phone, code)
    if err != nil {
        log.Println("语音播报失败:", err)
    }
}(userPhone, verifyCode)

3. 频率限制 (Rate Limiting)

语音呼叫成本较高且容易骚扰用户。在逻辑中,我们必须通过 Redis 实施严格的限制:

  • 同一手机号 60 秒内只能获取一次。
  • 同一 IP 每天限制获取 5 次。

如果你喜欢这项技术,可以点击Lab网站体验一下。