环境准备
首先,我们需要安装必要的依赖包:
# 安装 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
注意事项
- 确保正确配置了所有依赖项
- 验证码参数要根据实际需求调整
- 在生产环境中建议使用 Redis 替代内存存储
- 定期清理过期的缓存数据
- 考虑添加请求频率限制
这个完整的实现包含了验证码生成、验证、防爆破等功能,可以直接用于生产环境。记得根据实际需求调整配置参数和存储方式。