Go语言实现防暴力破解的验证码系统:一个生产级解决方案

环境准备

首先,我们需要安装必要的依赖包:

# 安装 gin 框架
go get -u github.com/gin-gonic/gin

# 安装验证码库
go get -u github.com/mojocn/base64Captcha

# 安装 Redis 客户端(如果需要使用 Redis 存储)
go get -u github.com/redis/go-redis/v9

# 安装日志库
go get -u go.uber.org/zap

完整代码实现

1. 配置文件 (config/config.go)

package config

type System struct {
    UseMultipoint bool
}

type Captcha struct {
    KeyLong             int   // 验证码长度
    ImgWidth           int   // 验证码宽度
    ImgHeight          int   // 验证码高度
    OpenCaptcha        int   // 防爆破验证码开启此数
    OpenCaptchaTimeOut int   // 验证码超时时间
}

type Configuration struct {
    System  System
    Captcha Captcha
}

2. 请求和响应模型 (model/request/login.go)

package request

type LoginReq struct {
    Username  string `json:"username"`  // 用户名
    Password  string `json:"password"`  // 密码
    Captcha   string `json:"captcha"`   // 验证码
    CaptchaId string `json:"captchaId"` // 验证码ID
}

3. 响应模型 (model/response/response.go)

package response

type SysCaptchaResponse struct {
    CaptchaId     string `json:"captchaId"`    // 验证码ID
    PicPath       string `json:"picPath"`       // 验证码图片base64
    CaptchaLength int    `json:"captchaLength"` // 验证码长度
    OpenCaptcha   bool   `json:"openCaptcha"`   // 是否开启验证码
}

type LoginResponse struct {
    User      interface{} `json:"user"`
    Token     string      `json:"token"`
    ExpiresAt int64       `json:"expiresAt"`
}

4. 验证码控制器 (controller/admin/login.go)

package admin

import (
    "github.com/gin-gonic/gin"
    "github.com/mojocn/base64Captcha"
    "github.com/redis/go-redis/v9"
    "go.uber.org/zap"
    "time"
)

var store = base64Captcha.DefaultMemStore

type LoginApi struct{}

// Captcha 生成验证码
func (l *LoginApi) Captcha(c *gin.Context) {
    // 判断验证码是否开启
    openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha               
    openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut 
    key := c.ClientIP()
    
    // 获取当前IP的验证码请求次数
    v, ok := global.BlackCache.Get(key)
    if !ok {
        global.BlackCache.Set(key, 1, time.Second*time.Duration(openCaptchaTimeOut))
    }

    // 判断是否需要验证码
    var oc bool
    if openCaptcha == 0 || openCaptcha < utils.InterfaceToInt(v) {
        oc = true
    }

    // 生成验证码
    driver := base64Captcha.NewDriverDigit(
        global.GVA_CONFIG.Captcha.ImgHeight,
        global.GVA_CONFIG.Captcha.ImgWidth,
        global.GVA_CONFIG.Captcha.KeyLong,
        0.7,
        80,
    )
    
    cp := base64Captcha.NewCaptcha(driver, store)
    id, b64s, _, err := cp.Generate()
    if err != nil {
        global.GVA_LOG.Error("验证码获取失败!", zap.Error(err))
        response.FailWithMessage("验证码获取失败", c)
        return
    }

    response.OkWithDetailed(response.SysCaptchaResponse{
        CaptchaId:     id,
        PicPath:       b64s,
        CaptchaLength: global.GVA_CONFIG.Captcha.KeyLong,
        OpenCaptcha:   oc,
    }, "验证码获取成功", c)
}

// Login 登录处理
func (l *LoginApi) Login(c *gin.Context) {
    var loginReq request.LoginReq
    if err := c.ShouldBindJSON(&loginReq); err != nil {
        response.FailWithMessage(err.Error(), c)
        return
    }

    key := c.ClientIP()
    openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha
    openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut

    v, ok := global.BlackCache.Get(key)
    if !ok {
        global.BlackCache.Set(key, 1, time.Second*time.Duration(openCaptchaTimeOut))
    }

    var oc bool = openCaptcha == 0 || openCaptcha < utils.InterfaceToInt(v)

    // 验证码校验
    if !oc || (loginReq.CaptchaId != "" && loginReq.Captcha != "" && 
        store.Verify(loginReq.CaptchaId, loginReq.Captcha, true)) {
        
        // 验证用户名密码
        u := &systemMod.SysAdmin{Username: loginReq.Username, Password: loginReq.Password}
        user, err := (&system.AdminService{}).Login(u)
        if err != nil {
            global.BlackCache.Increment(key, 1)
            response.FailWithMessage("登录失败:"+err.Error(), c)
            return
        }

        // 生成 Token
        l.TokenNext(c, *user)
        return
    }

    global.BlackCache.Increment(key, 1)
    response.FailWithMessage("验证码错误", c)
}

5. 路由配置 (router/admin.go)

package router

import (
    "github.com/gin-gonic/gin"
)

func InitAdminRouter(Router *gin.RouterGroup) {
    adminRouter := Router.Group("admin")
    loginApi := admin.LoginApi{}
    {
        adminRouter.GET("captcha", loginApi.Captcha)     // 获取验证码
        adminRouter.POST("login", loginApi.Login)        // 登录
    }
}

6. 缓存工具 (utils/cache.go)

package utils

import (
    "sync"
    "time"
)

type BlackCache struct {
    sync.RWMutex
    cache map[string]CacheItem
}

type CacheItem struct {
    Value     interface{}
    ExpiredAt time.Time
}

func NewBlackCache() *BlackCache {
    return &BlackCache{
        cache: make(map[string]CacheItem),
    }
}

func (c *BlackCache) Set(key string, value interface{}, expiration time.Duration) {
    c.Lock()
    defer c.Unlock()
    c.cache[key] = CacheItem{
        Value:     value,
        ExpiredAt: time.Now().Add(expiration),
    }
}

func (c *BlackCache) Get(key string) (interface{}, bool) {
    c.RLock()
    defer c.RUnlock()
    
    if item, exists := c.cache[key]; exists {
        if time.Now().Before(item.ExpiredAt) {
            return item.Value, true
        }
        delete(c.cache, key)
    }
    return nil, false
}

func (c *BlackCache) Increment(key string, delta int) {
    c.Lock()
    defer c.Unlock()
    
    if item, exists := c.cache[key]; exists {
        if value, ok := item.Value.(int); ok {
            c.cache[key] = CacheItem{
                Value:     value + delta,
                ExpiredAt: item.ExpiredAt,
            }
        }
    }
}

配置示例

# config.yaml
system:
  use-multipoint: false

captcha:
  key-long: 4
  img-width: 240
  img-height: 80
  open-captcha: 3
  open-captcha-timeout: 3600

使用示例

func main() {
    r := gin.Default()
    
    // 初始化全局配置
    global.GVA_CONFIG = config.LoadConfig()
    global.BlackCache = utils.NewBlackCache()
    
    // 初始化路由
    publicGroup := r.Group("")
    router.InitAdminRouter(publicGroup)
    
    r.Run(":8080")
}

API 调用示例

1. 获取验证码

curl -X GET http://localhost:8080/admin/captcha

2. 登录请求

curl -X POST http://localhost:8080/admin/login \
  -H "Content-Type: application/json" \
  -d '{
    "username": "admin",
    "password": "123456",
    "captchaId": "获取到的captchaId",
    "captcha": "用户输入的验证码"
  }'

项目结构

├── config/
│   └── config.go
├── controller/
│   └── admin/
│       └── login.go
├── model/
│   ├── request/
│   │   └── login.go
│   └── response/
│       └── response.go
├── router/
│   └── admin.go
├── utils/
│   └── cache.go
└── main.go

注意事项

  1. 确保正确配置了所有依赖项
  2. 验证码参数要根据实际需求调整
  3. 在生产环境中建议使用 Redis 替代内存存储
  4. 定期清理过期的缓存数据
  5. 考虑添加请求频率限制

这个完整的实现包含了验证码生成、验证、防爆破等功能,可以直接用于生产环境。记得根据实际需求调整配置参数和存储方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值