图形验证码后端实现

457 阅读1分钟

调研

网上有两种star比较多的开源库,分别是

github.com/mojocn/base…

github.com/dchest/capt…

两者star差不多,第一个是中国人写的,中文文档比较详细,所以使用第一个

初始化

把自定义的redis当作store,新建一个结构体实现cap.store接口的方法

var c *cap.Captcha

func Init(store cap.Store) {
	c = cap.NewCaptcha(cap.NewDriverString(60, 200, 0, 1, 4, "1234567890qwertyuioplkjhgfdsazxcvbnm", &color.RGBA{128, 128, 128, 3}, nil, nil), store)
}

生成验证码

func Generate() (id, b64s string, err error) {
	id, b64s, err = c.Generate()
	if err != nil {
		fmt.Println(err)
		return
	}
	return
}

校验

func Verify(id, verifyVal string, clear bool) bool {
	//防止缓存没有值,参数为空导致的校验成功的异常case
	if verifyVal == "" {
		log.Warn("[captcha] Verify param wrong", zap.String("id", id), zap.String("verifyVal", verifyVal))
		return false
	}
	return c.Verify(id, verifyVal, clear)
}

自定义store

实现默认store的方法

const (
	storeExp = time.Minute
)

type Re struct {
	rdb *redis.Client
}

func RedisStore(redi *redis.Client) (r *Re) {
	if redi == nil {
		fmt.Println("redis init failed ")
		return nil
	}
	r = &Re{}
	r.rdb = redi
	return r
}

func (r *Re) Set(id string, value string) error {
	if r.rdb == nil {
		log.Error("[captcha] redis cli nil")
		return fmt.Errorf("[captcha] redis cli nil")
	}
	_, err := r.rdb.Set(context.Background(), id, value, storeExp).Result()
	if err != nil {
		log.Error("[captcha] redis set failed", zap.Error(err))
		return err
	}
	return nil
}

// Get returns stored digits for the captcha id. Clear indicates
// whether the captcha must be deleted from the store.
func (r *Re) Get(id string, clear bool) string {
	if r.rdb == nil {
		log.Error("[captcha] redis cli nil")
		return ""
	}
	res, err := r.rdb.Get(context.Background(), id).Result()
	if err != nil {
		log.Error("[captcha] redis get failed",zap.Error(err))
		return ""
	}
	if clear {
		_, err := r.rdb.Del(context.Background(), id).Result()
		if err != nil {
			//可能导致二次校验也成功
			log.Error("[captcha] redis del failed",zap.Error(err))
			return res
		}
	}
	return res
}

// Verify captcha's answer directly
func (r *Re) Verify(id, answer string, clear bool) bool {
	return answer == r.Get(id, clear)
}

考虑

1.要不要限制前端刷新次数,不然后端会一直往redis里面存储,只能自然过期,不过占用空间应该很小