一文搞懂 Go 整合 captcha 实现验证码功能
 作者:Barry Yan
- 2022-10-15  北京
 本文字数:3258 字
阅读完需:约 1 分钟

最近在使用 Go 语言搞一个用户登录 &注册的功能,说到登录 &注册相关,我们油然会产生一种增加验证码的想法,因此着手实现,后来在 GitHub 上找到了这个名叫 captcha 的插件,于是就利用文档进行了初步的学习,并融入到自己的项目中,整个过程下来感觉这个插件的设计非常巧妙,所以就想写一篇文章分享一下,通过本篇文章,你会学到:
利用 captcha 生成验证码返回到前端使用
将 captcha 生成的验证码点击刷新
将 captcha 生成的验证码利用 Redis 进行缓存
1 captcha 概述
GitHub:https://github.com/dchest/captcha
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 接口时需要完整实现,也就是能真正的实现存储或缓存功能,否则验证码无法生成
参考:
https://github.com/dchest/captcha/blob/master/capexample/main.go
划线
评论
复制
发布于: 刚刚阅读数: 9
版权声明: 本文为 InfoQ 作者【Barry Yan】的原创文章。
原文链接:【http://xie.infoq.cn/article/46bc4071549b55c66ba691847】。文章转载请联系作者。
Barry Yan
关注
做兴趣使然的Hero 2021-01-14 加入
Just do it.










    
评论