前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >3 分钟用 Go 写完验证码,面试官现场发 Offer

3 分钟用 Go 写完验证码,面试官现场发 Offer

作者头像
小锟哥哥
发布2022-05-10 08:55:47
4790
发布2022-05-10 08:55:47
举报
文章被收录于专栏:GoLang全栈

我记得在17年那会儿网站登录注册这些随处都可以看到下面这种验证码:

但是好像近些年,这种验证码消失了,出现了特别多的人机验证,如下图:

真的是要感叹技术的发展真的不要太爽了。

人机校验

可能很多同学还不知道他的用处:

他最大的用处就是鉴别是人在操作你的产品还是机器。

比如:最常见的注册、登录业务,如果你不上一些验证手段,可能人家就可以随便找一个工具,直接对你注册接口疯狂输出,让你分分钟拥有十几万神秘用户。

在人机验证没出来之前,人们用的最多的就是用验证码来拦截,要注册或者登录,必须输入验证码里面的内容。

但是随着图片识别技术的发展,这种技术几乎已经失守。

但是一看价格:

以上阿里和腾讯两家的价格,不算特别便宜哈。

前后端分离下实现验证码服务的逻辑

虽然人机校验好处多多,但是介于价格可能很多公司或者个人还是会望而却步。

其实刚上的新服务,前期还是可以先使用验证码来鉴别的,到中后期再接入人机也是可以的。

验证码的逻辑

在传统的单体服务,非前后端分离的情况下,我们可以使用 session 来存储,整个流程可以像下图这样走:

但是现在都前后端分离了,请求会话都是无状态的,该怎么实现呢?

实现方式可能有很多,但是我个人建议可以借鉴下人机交互的逻辑,如下图所示:

这里我们把会话和验证码分离开了,只要需要用到验证码的地方,都可以去请求这个接口,在下一次请求的时候带上返回的 key 和输入的值就可以了。

这个流程其实还是有很多漏洞在里面,实际上生产肯定不能直接这么简单的上,还要加上很多其他技术在里面,比如把生成的验证码和下一步请求的地址关联起来、签名呀这些。

但是这已经能拦截一大批攻击者了。

这种做法和人机验证最大的区别在于,我们生产的验证码容易别人用工具识别出来,人机验证的他们有一套算法去防止被机器识别出来。

后期如果要换成人机也非常容易,因为流程是一样的。

基于 Gin 实现一套验证码

上面说了那么多的理论逻辑,下面开始上代码:

完整的代码可以到我们官方的 Github 库查看:

代码语言:javascript
复制
https://github.com/GoLangStackDev/captcha-demo.git

使用到的库

这里我们处理 Gin 之外还要用到 captcha 库:

官方 GitHub 地址:github.com/dchest/captcha

这个库功能非常强大,他支持生成图片验证码和音频验证码:

实现思路一样的,代码几乎一样,只是类型不一样,我们主要以生成图片为准。

安装库

我们需要安装两个库:

代码语言:javascript
复制
go get github.com/dchest/captcha
go get github.com/gin-gonic/gin

先实现工具类

这个工具类我们专门用来处理验证码:

代码语言:javascript
复制
// Captcha 方便后期扩展
type Captcha struct {}

// 单例
var captchaInstance *Captcha
func Instance() *Captcha {
 if captchaInstance==nil {
  captchaInstance = &Captcha{}
 }
 return captchaInstance
}

我们声明了一个结构体,方便后期在 captcha 这个库上进行扩展。

代码语言:javascript
复制
// CreateImage 创建图片验证码
func (this *Captcha) CreateImage() string {
 length := captcha.DefaultLen
 captchaId := captcha.NewLen(length)
 return captchaId
}

创建验证码也很容易,我们这里直接全部使用他默认的配置,生产6位数的数字验证码,后期有需要可以参考 captcha 库进行调整配置。

这里会返回一个 ID 给我们,这个 ID 就是刚我画的流程图里面的 key,他关联了一个随机数,也就是图片的数字。

这里他存放在哪里的呢?

默认是内存,所以重启程序后就可能找不到已经生成的验证码了,但你可以修改他存放在哪里。

代码语言:javascript
复制
// Reload 重载
func (this *Captcha) Reload(captchaId string) bool {
 return captcha.Reload(captchaId)
}

因为不可能用户每次都能输对,所以有些时候用户不能识别的情况下就需要进行重新生成随机数,也就是重新生成一张图片,但是 key 也就是 ID 是不能变的,此时就要用到重载。

代码语言:javascript
复制
// Verify 验证
func (this *Captcha) Verify(captchaId,val string) bool {
 return captcha.VerifyString(captchaId, val)
}

这就是验证了,传入 ID 和 用户输入的值就可验证了。

代码语言:javascript
复制
// 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 库他会生成一个图片的二进制流,你只需要把这个二进制流返回回去即可得到图片。

Gin部分的代码

这里都只展示关键部分的代码:

代码语言:javascript
复制
// 创建
// 这里方便看到效果 我用的 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,
  })
})

首先是创建的接口,这里直接调用我们工具类的 CreateImage 方法拿到 key 即可。

这里的 URL 和下面这个现实的 API 关联。

代码语言:javascript
复制
// 现实图片
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))
})

我们每请求一次这个 key 就重载刷新一下他的 Code,方便前端刷新。

前端只需要在这个地址后面加上随机参数即可实现刷新验证码。

最关键的地方就是要设置客户端的请求头里面不能让他缓存。

代码语言:javascript
复制
// 校验
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})
 }
})

最后就是校验了,正常来说这个接口是不能放出来了的,因为:

1、 captcha 库,只要校验一次,不管成功失败他的 ID 就失效了。

2、我们一般都只在业务里面去校验。

最后来看下效果吧:

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-11-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 GoLang全栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 人机校验
  • 前后端分离下实现验证码服务的逻辑
    • 验证码的逻辑
    • 基于 Gin 实现一套验证码
      • 使用到的库
        • 安装库
          • 先实现工具类
            • Gin部分的代码
            相关产品与服务
            验证码
            腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档