前言

验证码是一个除了限流外很有效的针对恶意爬虫的办法,很不巧在最最近频繁遭到暴力破解,忍无可忍使用了验证码来防止刷接口。

base64Captcha

这是一个go的图片验证码库,使用方法也很简单。

使用

安装命令

go get -u github.com/mojocn/base64Captcha

然后使用的话,先开辟一个验证码使用的存储空间

var store = base64Captcha.DefaultMemStore
// base64Captcha.DefaultMemStore是默认的:10240 个 过期时间10分钟
// 也可以自己设定参数base64Captcha.NewMemoryStore(GCLimitNumber, Expiration)

创建验证码驱动
分为5种不同类型的图形验证码

  1. dight 数字验证码
  2. audio 语音验证码
  3. string 字符验证码
  4. math 数学验证码(加减乘除)
  5. chinese中文验证码-有bug

// 生成默认数字的driver

driver := base64Captcha.DefaultDriverDigit
// 也可以自定义参数
base64Captcha.NewDriverDigit(height , width , length , maxSkew , dotCount)

// 例子:
	driver := base64Captcha.NewDriverDigit(80, 240, 6, 0.7, 80)

生成验证码,同时保存至store

c := base64Captcha.NewCaptcha(driver, store)

// 生成base64图像验证及id
id, b64s, err := c.Generate()

Store Interface

多台服务器需要共享验证码存储,一般为了效率也首选redis,指定存储store需要实现这个接口。

// Store An object implementing Store interface can be registered with SetCustomStore
// function to handle storage and retrieval of captcha ids and solutions for
// them, replacing the default memory store.
//
// It is the responsibility of an object to delete expired and used captchas
// when necessary (for example, the default memory store collects them in Set
// method after the certain amount of captchas has been stored.)
type Store interface {
	// Set sets the digits for the captcha id.
	Set(id string, value string)

	// Get returns stored digits for the captcha id. Clear indicates
	// whether the captcha must be deleted from the store.
	Get(id string, clear bool) string

	//Verify captcha's answer directly
	Verify(id, answer string, clear bool) bool
}

实现三个方法即可

func NewDefaultRedisStore() *RedisStore {
	return &RedisStore{
		Expiration: time.Second * 180,
		PreKey:     "SALMON_CAPTCHA_",
	}
}

type RedisStore struct {
	Expiration time.Duration
	PreKey     string
	Context    context.Context
}


func (rs *RedisStore) UseWithCtx(ctx context.Context) base64Captcha.Store {
	rs.Context = ctx
	return rs
}

// 实现store接口

func (rs *RedisStore) Set(id string, value string) error {
	err := db.RedisClient.Set(rs.Context, rs.PreKey+id, value, rs.Expiration).Err()
	if err != nil {
		fmt.Println(err)
	}
	return nil
}

func (rs *RedisStore) Get(key string, clear bool) string {
	val, err := db.RedisClient.Get(rs.Context, key).Result()
	if err != nil {
		return ""
	}
	if clear {
		err := db.RedisClient.Del(rs.Context, key).Err()
		if err != nil {
			return ""
		}
	}
	return val
}

func (rs *RedisStore) Verify(id, answer string, clear bool) bool {
	key := rs.PreKey + id
	v := rs.Get(key, clear)
	return v == answer
}

验证验证码

type CaptCha struct {
	Captcha   string `json:"captcha"`   // 验证码
	CaptchaId string `json:"captchaId"` // 验证码ID
}

var data CaptCha

data.Captcha = "xxx"
data.CaptchaId = "xxx"

if store.Verify(data.CaptchaId, data.Captcha, true){
  fmt.Println("验证码正确")
}else{
  fmt.Println("验证码错误")
}

注意:在上面自定义了RedisStore,将其增加了一个前缀存到了redis中,这也是为了和其他用到redis的地方区分开,但是不重要,在验证时无需加入前缀。 正常使用即可。