一文搞懂Go整合captcha实现验证码功能

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Redis 版,经济版 1GB 1个月
简介: 一文搞懂Go整合captcha实现验证码功能

最近在使用Go语言搞一个用户登录&注册的功能,说到登录&注册相关,我们油然会产生一种增加验证码的想法,因此着手实现,后来在GitHub上找到了这个名叫captcha的插件,于是就利用文档进行了初步的学习,并融入到自己的项目中,整个过程下来感觉这个插件的设计非常巧妙,所以就想写一篇文章分享一下,通过本篇文章,你会学到:

  • 利用captcha生成验证码返回到前端使用
  • 将captcha生成的验证码点击刷新
  • 将captcha生成的验证码利用Redis进行缓存

1 captcha概述

GitHub:github.com/dchest/capt…

captcha的使用设计流程

网络异常,图片无法展示
|


2 实现代码(使用内存缓存)

2.1 后端代码

生成验证码图片API:

//GenerateImg 生成验证码图片名称
func GenerateImg(w http.ResponseWriter, req *http.Request) {
   w.Header().Set("Access-Control-Allow-Origin", "*")             //允许访问所有域
   w.Header().Add("Access-Control-Allow-Headers", "Content-Type") 
   d := struct {
      CaptchaId string
   }{
      captcha.New(),
   }
   bytes, _ := json.Marshal(map[string]interface{}{"code": 0, "msg": "", "count": 0, "data": d.CaptchaId})
   w.Write(bytes)
}
复制代码

HTTP服务:

func RunHttp(port string) {
   logger := log.Default()
   http.Header{}.Set("Access-Control-Allow-Origin", "*")
   http.HandleFunc("/user/login", controller.UserLogin) //登录API
   http.HandleFunc("/img", controller.GenerateImg)  //生成验证码图片API
   http.Handle("/verify/", captcha.Server(captcha.StdWidth, captcha.StdHeight)) //刷新验证码API
   logger.Println("Http Server Running port:", port, "...")
   http.ListenAndServe(":"+port, nil)
}
复制代码

启动HTTP服务:

func main() {
   web.RunHttp("8000")
}
复制代码

验证码验证:

//UserLogin 用户登录
func UserLogin(w http.ResponseWriter, req *http.Request) {
   w.Header().Set("Access-Control-Allow-Origin", "*")
   w.Header().Add("Access-Control-Allow-Headers", "Content-Type")
   ......
   var m map[string]string
   body, err := ioutil.ReadAll(req.Body)
   if err != nil {
      panic(err)
   }
   json.Unmarshal(body, &m)
   var k = m["verify_key"]
   var v = m["verify_value"]
   res := captcha.VerifyString(k, v)
   if res { // 验证通过
     ......
   } else { // 验证未通过
     ......
   }
   ......
}
复制代码

2.2 前端代码

......
<form class="layui-form" id="form">
    <h3 style="font-size: 20px;text-align: center;margin-bottom: 30px;">登录</h3>
    <div class="layui-form-item">
        <label class="layui-form-label">账号</label>
        <div class="layui-input-inline">
            <input type="text" id="loginName" placeholder="请输入账号" autocomplete="off"
                   class="layui-input">
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">密码</label>
        <div class="layui-input-inline">
            <input type="password" id="loginPwd" placeholder="请输入密码" autocomplete="off"
                   class="layui-input">
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">验证码</label>
        <div class="layui-input-inline">
            <input type="text" id="loginV" placeholder="请输入验证码" autocomplete="off"
                   class="layui-input">
        </div>
    </div>
    <div class="layui-form-item">
        <div class="layui-input-block">
            <button class="layui-btn" type="button" onclick="login()">立即提交</button>
            <button type="button" onclick="toRegister()" class="layui-btn layui-btn-primary">注册</button>
        </div>
    </div>
</form>
<img id="verify" onclick="reload()"></img>
......
<input type="hidden" id="verify_key">
</body>
<script src="//unpkg.com/layui@2.6.8/dist/layui.js"></script>
<script src="//cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script>
    const base_url = 'http://localhost:8000'
    init()
    function init() {
        $.ajax({
            url: base_url + "/img",
            type: "GET",
            success: function (res) {
                var obj = JSON.parse(res)
                $("#verify").attr("src", base_url + "/verify/" + obj.data + ".png")
                $("#verify_key").attr("value", obj.data)
            }
        })
    }
    function reload() {
        var url = $("#verify").attr("src");
        $("#verify").attr("src", url + "?reload=" + (new Date()).getTime())
    }
    function login() {
        var loginName = $("#loginName").val()
        var loginPwd = $("#loginPwd").val()
        var verify_key = $("#verify_key").val()
        var loginV = $("#loginV").val()
        var data = {
            'login_name': loginName,
            'pwd': loginPwd,
            'verify_key': verify_key,
            'verify_value': loginV
        }
        $.ajax({
            url: base_url + "/user/login",
            type: "POST",
            data: JSON.stringify(data),
            success: function (res) {
               ......
            },
            ......
        })
    }
   ......
</script>
复制代码

2.3 注意点

  • 跨域问题:可加入如下代码
w.Header().Set("Access-Control-Allow-Origin", "*")             //允许访问所有域
w.Header().Add("Access-Control-Allow-Headers", "Content-Type") 
复制代码

3 自定义Store(使用Redis缓存)

3.1 自定义对象并实现Store抽象

Redis初始化:

var (
   RDB          *redis.Client
   TokenTimeOut = time.Second * 3600
)
func init() {
   RDB = redis.NewClient(&redis.Options{
      Addr:     "127.0.0.1:6379",
      Password: "",
      DB:       0,
   })
}
复制代码

自定义结构体&实现Store抽象:

type StoreImpl struct {
   RDB        *redis.Client
   Expiration time.Duration
}
func (impl *StoreImpl) Set(id string, digits []byte) {
   impl.RDB.Set(context.Background(), id, string(digits), impl.Expiration)
}
func (impl *StoreImpl) Get(id string, clear bool) (digits []byte) {
   bytes, _ := impl.RDB.Get(context.Background(), id).Bytes()
   return bytes
}
复制代码

3.2 配置captcha,加入自定义Store实现

//GenerateImg 生成验证码图片名称
func GenerateImg(w http.ResponseWriter, req *http.Request) {
   w.Header().Set("Access-Control-Allow-Origin", "*")             //允许访问所有域
   w.Header().Add("Access-Control-Allow-Headers", "Content-Type") //header的类型
   //需要在New之前进行指定
   captcha.SetCustomStore(&verify.StoreImpl{
      RDB:        dao.RDB,
      Expiration: time.Second * 1000,
   })
   d := struct {
      CaptchaId string
   }{
      captcha.New(),
   }
   bytes, _ := json.Marshal(map[string]interface{}{"code": 0, "msg": "", "count": 0, "data": d.CaptchaId})
   w.Write(bytes)
}
复制代码

3.3 注意点

  • 需要在captcha.New()之前进行captcha.SetCustomStore()
  • 在captcha.SetCustomStore()之后,自定义的方法实现Store接口时需要完整实现,也就是能真正的实现存储或缓存功能,否则验证码无法生成

参考:

github.com/dchest/capt…


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
1月前
|
JSON Go 数据格式
从1开始,扩展Go语言后端业务系统的RPC功能
从1开始,扩展Go语言后端业务系统的RPC功能
48 0
|
1月前
|
存储 Go
Go 浅析主流日志库:从设计层学习如何集成日志轮转与切割功能
本文将探讨几个热门的 go 日志库如 logrus、zap 和官网的 slog,我将分析这些库的的关键设计元素,探讨它们是如何支持日志轮转与切割功能的配置。
172 0
Go 浅析主流日志库:从设计层学习如何集成日志轮转与切割功能
|
1月前
|
网络协议 Go Windows
Wireshark的Go 和Capture 功能都能做什么?
Wireshark的Go 和Capture 功能都能做什么?
|
7天前
|
Go
Go 中使用切片来实现动态数组的功能
Go 中使用切片来实现动态数组的功能
|
1月前
|
算法 Java 编译器
GO语言中的runtime功能概要
【5月更文挑战第17天】本文简介Go语言的`runtime`库支撑着高效的并发和内存管理。此外,runtime还涉及定时器、错误处理(Panic和Recover)以及反射功能。通过内联展开和逃逸分析等手段,实现性能优化。
47 1
|
10月前
|
Java Go 调度
Go Runtime功能初探
Go Runtime功能初探
70 2
|
1月前
|
缓存 NoSQL 前端开发
一文搞懂Go整合captcha实现验证码功能
一文搞懂Go整合captcha实现验证码功能
40 0
|
6月前
|
存储 算法 Go
如何使用 Go 语言实现查找重复行的功能?
如何使用 Go 语言实现查找重复行的功能?
150 0
如何使用 Go 语言实现查找重复行的功能?
|
1月前
|
JSON NoSQL Go
|
8月前
|
搜索推荐 中间件 Go
Go语言微服务框架 - 11.接口的参数校验功能-buf中引入PGV
大量开发接口的朋友会经常遇到**接口参数校验**的问题。举个例子,我们希望将某个字段是必填的,如`name`,我们经常会需要做两步: 1. 在程序中加一个**判断逻辑**,当这个字段为空时返回错误给调用方 2. 在接口文档中加上**注释**,告诉调用方这个参数必填 一旦某项工作被拆分为两步,就很容易出现**不一致性**:对应到参数检查,我们会经常遇到文档和具体实现不一致,从而导致双方研发的沟通成本增加。那么,今天我将引入一个方案,实现两者的一致性。
77 0