在现代应用开发中,手机验证码验证功能已成为用户身份认证和安全验证的常见手段,尤其是在登录注册、密码重置等场景中。本文将重点讲解如何使用 Golang 实现一个简单、高效的手机验证码功能,并探讨其在后端的存储和验证流程。
在此方案中,我们将结合常用工具(如 Redis 和第三方短信服务)提供完整的实现示例。
一、功能需求分析
功能需求:
用户通过手机号请求发送验证码。
后端生成随机验证码并通过第三方短信服务发送至用户手机。
后端存储验证码及其有效期(建议使用缓存工具,如 Redis)。
前端用户输入验证码进行提交,后端验证用户输入的验证码是否正确与有效。
安全性需求:
验证码应尽量短期有效(如 5 分钟)。
用户连续输入验证码错误达到一定次数时,限制进一步验证请求。
验证成功后,验证码应即刻失效,避免重复使用。
二、技术选型
语言:Golang
短信发送:
使用阿里云、Twilio 等成熟的第三方短信服务完成短信发送功能。
验证码存储:
使用 Redis 高性能缓存解决方案存储验证码,便于快速读写和设置自动过期时间。
三、实现验证码功能
下面我们通过代码实现从生成验证码到后端验证的完整流程。
1. 构建项目目录结构
项目目录大致如下:
/go-otp-auth
├── main.go # 主入口
├── redis.go # Redis 初始化与操作
├── otp.go # 验证码生成与验证逻辑
├── sms.go # 第三方短信服务集成
├── go.mod # Go 模块配置
2. 编写 Redis 工具模块 (redis.go
)
Redis 用于存储验证码及其过期时间,确保高性能的读写操作。
package main
import (
"context"
"log"
"time"
"github.com/go-redis/redis/v8"
)
var ctx = context.Background()
// 初始化 Redis 客户端
func InitRedis() *redis.Client {
client := redis.NewClient(&redis.Options{
Addr: "localhost:6379", // Redis 地址
Password: "", // Redis 密码(默认为空)
DB: 0, // 默认 DB
})
_, err := client.Ping(ctx).Result()
if err != nil {
log.Fatalf("Failed to connect Redis: %v", err)
}
return client
}
// 全局 Redis 客户端
var RedisClient = InitRedis()
// 将数据存储到 Redis
func StoreToRedis(key string, value string, expiration time.Duration) error {
err := RedisClient.Set(ctx, key, value, expiration).Err()
if err != nil {
return err
}
return nil
}
// 从 Redis 获取数据
func GetFromRedis(key string) (string, error) {
val, err := RedisClient.Get(ctx, key).Result()
if err != nil {
return "", err
}
return val, nil
}
// 删除 Redis 数据
func DeleteFromRedis(key string) error {
err := RedisClient.Del(ctx, key).Err()
if err != nil {
return err
}
return nil
}
3. 编写验证码模块 (otp.go
)
此模块负责生成随机验证码,并封装验证码的存储及验证逻辑。
package main
import (
"fmt"
"math/rand"
"time"
)
// 初始化随机数种子
func init() {
rand.Seed(time.Now().UnixNano())
}
// 生成 6 位随机验证码
func GenerateRandomCode(length int) string {
code := ""
for i := 0; i < length; i++ {
code += fmt.Sprintf("%d", rand.Intn(10)) // 生成 0-9 的随机数
}
return code
}
// 保存验证码到 Redis(过期时间为 5 分钟)
func SaveOTPToRedis(phone string, code string) error {
expiration := 5 * time.Minute
return StoreToRedis(phone, code, expiration)
}
// 验证用户输入的验证码
func ValidateOTP(phone string, inputCode string) (bool, error) {
// 从 Redis 获取存储的验证码
storedCode, err := GetFromRedis(phone)
if err != nil {
return false, fmt.Errorf("verification code expired or not existed")
}
// 比较验证码是否一致
if inputCode == storedCode {
// 验证成功后删除 Redis 中的验证码
DeleteFromRedis(phone)
return true, nil
}
return false, fmt.Errorf("verification code is incorrect")
}
4. 编写短信服务模块 (sms.go
)
这里以阿里云短信服务为例,调用其 API 实现验证码短信发送。实际开发中需要填写具体的 API 配置。
package main
import (
"fmt"
)
// 模拟短信发送(伪代码,替换为实际短信 API 调用)
func SendSMS(phone string, code string) error {
// 这里实现短信发送逻辑 (如通过阿里云短信服务,Twilio API 等)
message := fmt.Sprintf("Your verification code is: %s", code)
fmt.Printf("Sending SMS to %s: %s\n", phone, message)
// 假设发送成功:
return nil
}
5. 实现主入口 (main.go
)
通过 HTTP 接口模拟用户操作请求验证码和验证验证码的处理逻辑。
package main
import (
"fmt"
"net/http"
)
// 发送验证码接口
func SendOTPHandler(w http.ResponseWriter, r *http.Request) {
phone := r.URL.Query().Get("phone") // 获取手机号
if phone == "" {
http.Error(w, "Phone number is required", http.StatusBadRequest)
return
}
// 生成验证码
code := GenerateRandomCode(6)
// 发送验证码短信
err := SendSMS(phone, code)
if err != nil {
http.Error(w, "Failed to send SMS", http.StatusInternalServerError)
return
}
// 保存验证码到 Redis
err = SaveOTPToRedis(phone, code)
if err != nil {
http.Error(w, "Failed to store OTP", http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "OTP sent successfully to %s", phone)
}
// 验证验证码接口
func ValidateOTPHandler(w http.ResponseWriter, r *http.Request) {
phone := r.URL.Query().Get("phone")
code := r.URL.Query().Get("code")
if phone == "" || code == "" {
http.Error(w, "Phone number and code are required", http.StatusBadRequest)
return
}
// 验证验证码
valid, err := ValidateOTP(phone, code)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if valid {
fmt.Fprintf(w, "Verification succeeded!")
} else {
http.Error(w, "Invalid verification code", http.StatusUnauthorized)
}
}
func main() {
http.HandleFunc("/send-otp", SendOTPHandler)
http.HandleFunc("/validate-otp", ValidateOTPHandler)
fmt.Println("Server is running on :8080...")
http.ListenAndServe(":8080", nil)
}
四、测试与验证
启动服务后,用浏览器或 Postman 测试以下接口:
http://localhost:8080/send-otp?phone=1234567890
:发送验证码。http://localhost:8080/validate-otp?phone=1234567890&code=123456
:验证验证码。
确保 Redis 中存储的验证码按预期工作,并在过期或验证后清除。
五、总结
本文通过一个完整的示例展示了如何使用 Golang 实现手机验证码的功能。关键点包括:
安全性:验证码短期内有效,且验证成功后删除。
高性能:利用 Redis 作为临时存储,提升效率。
扩展性:短信服务通过模块化设计,可以轻松切换到其他服务。
评论