前后端分离下实现验证码服务的逻辑分析
人机交互模式
基于 Gin 实现一套验证码
具体来说,CAPTCHA库可以:
- 生成各种形式的CAPTCHA图像,例如文本、数字和数学表达式等。
- 添加噪声和扭曲效果以增加CAPTCHA的难度和安全性。
- 提供快速而可靠的CAPTCHA验证功能,以确保只有真正的人类用户能够通过验证。
该库还支持自定义CAPTCHA参数,例如图像大小、字体、颜色、背景等。
实现工具类
// Captcha 方便后期扩展
type Captcha struct {}
// 单例
var captchaInstance *Captcha
func Instance() *Captcha {
if captchaInstance==nil {
captchaInstance = &Captcha{}
}
return captchaInstance
}
声明了一个结构体,方便后期在 captcha 这个库上进行扩展
// CreateImage 创建图片验证码
func (this *Captcha) CreateImage() string {
length := captcha.DefaultLen
captchaId := captcha.NewLen(length)
return captchaId
}创建验证码也很容易,我们这里直接全部使用他默认的配置,生产6位数的数字验证码,后期有需要可以参考 captcha 库进行调整配置。
这里会返回一个 ID 给我们,这个 ID 就是刚我画的流程图里面的 key,他关联了一个随机数,也就是图片的数字。
重载
因为不可能用户每次都能输对,所以有些时候用户不能识别的情况下就需要进行重新生成随机数,也就是重新生成一张图片,但是 key 也就是 ID 是不能变的,此时就要用到重载。
// Reload 重载
func (this *Captcha) Reload(captchaId string) bool {
return captcha.Reload(captchaId)
}验证
// Verify 验证
func (this *Captcha) Verify(captchaId,val string) bool {
return captcha.VerifyString(captchaId, val)
}传输
把图片输出给用户,captcha 库他会生成一个图片的二进制流,你只需要把这个二进制流返回回去即可得到图片。
// GetImageByte 获取图片二进制流
func (this *Captcha) GetImageByte(captchaId string) []byte {
var content bytes.Buffer
err := captcha.WriteImage(&content, captchaId, captcha.StdWidth, captcha.StdHeight)
if err!=nil {
log.Println(err)
return nil
}
return content.Bytes()
}总结
// Captcha 方便后期扩展
type Captcha struct {}
var captchaInstance *Captcha
func Instance() *Captcha {
if captchaInstance==nil {
captchaInstance = &Captcha{}
}
return captchaInstance
}
// CreateImage 创建图片验证码
func (this *Captcha) CreateImage() string {
length := captcha.DefaultLen
captchaId := captcha.NewLen(length)
return captchaId
}
// Reload 重载
func (this *Captcha) Reload(captchaId string) bool {
return captcha.Reload(captchaId)
}
// Verify 验证
func (this *Captcha) Verify(captchaId,val string) bool {
return captcha.VerifyString(captchaId, val)
}
// GetImageByte 获取图片二进制流
func (this *Captcha) GetImageByte(captchaId string) []byte {
var content bytes.Buffer
err := captcha.WriteImage(&content, captchaId, captcha.StdWidth, captcha.StdHeight)
if err!=nil {
log.Println(err)
return nil
}
return content.Bytes()
}测试
func main() {
r := gin.New()
// 创建
// 这里方便看到效果 我用的 GET 请求,实际生产最好不要用 GET
r.Handle("GET", "/captcha/create", func(c *gin.Context) {
imgId := captcha.Instance().CreateImage()
c.JSON(http.StatusOK,
gin.H{
"code": 200,
"key": imgId,
"url": "/captcha/img/"+imgId,
})
})
// 现实图片
r.Handle("GET", "/captcha/img/:key", func(c *gin.Context) {
captchaId := c.Param("key")
c.Writer.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
c.Writer.Header().Set("Pragma", "no-cache")
c.Writer.Header().Set("Expires", "0")
c.Writer.Header().Set("Content-Type", "image/png")
// 重载一次
captcha.Instance().Reload(captchaId)
// 输出图片
c.Writer.Write(captcha.Instance().GetImageByte(captchaId))
})
// 校验
r.Handle("GET", "/captcha/verify/:key/:val", func(c *gin.Context) {
captchaId := c.Param("key")
val := c.Param("val")
if captcha.Instance().Verify(captchaId,val) {
c.JSON(http.StatusOK, gin.H{"code": 200})
}else{
c.JSON(http.StatusOK, gin.H{"code": 400})
}
})
r.Run(":9000")
}