我们用GO玩一下验证码

简介: 我们用GO玩一下验证码

我们用GO玩一下验证码

嗨,我是小魔童哪吒,咱们上次分享的GO 中 defer 的实现原理,再来回顾一下吧

  • 分享了defer是什么
  • 简单示意了栈和队列
  • defer的数据结构和实现原理,具体的源码展示
  • GO 中defer3 条规则

要是对 GO 中 defer 实现原理还有点兴趣的话,欢迎查看文章 GO 中 defer的实现原理

今天我们来分享一些使用 GO 实现小案例,咱们边玩边成长

GO 的验证码介绍

我们平时使用到的验证码大致分为这几种,咱们梳理一下:

  • 传统输入的形式

输入图片上的数字,文字,字母等等

  • 输入类型的图形验证码

这个主要是来打广告的

  • 纯行为验证码

例如,按照提示滑动等等

  • 图标选择与行为辅助的验证码

例如咱们买火车票的时候验证码,各种图标让你选

  • 点击式的图文验证与行为辅助

例如某宝的验证码

  • 智能验证码

例如,点触智能验证码

GO 验证码案例

我们今天就来玩一玩第一种,使用最多的一种验证码吧

会使用 GO 的这个验证码库来完成,github.com/dchest/captcha

若我们向C/C++一样,将很多的底层处理都是我们自己来封装来实现的话,那还是挺累人的,GO 这一点确实蛮好,有很多使用的包,咱们在使用之余,也可以站在巨人的肩膀人,学习源码中的实现方式,学习大佬们的设计思想。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lC6WxucK-1625666538870)(https://i.loli.net/2021/06/19/q1joYsKZCSmT7Ep.gif)]

安装captcha

大家使用如下命令就可以下载下来使用

go get github.com/dchest/captcha

当我们在GOLAND 中用到 captcha库的时候,咱们可以看看源码目录

源码目录

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lAmTlVq9-1625666538873)(https://i.loli.net/2021/06/18/na2EQkyJZtxi1Pw.png)]

  • 有源码的具体使用案例
  • 具体的示例图片
  • 相关音频处理的实现
  • 验证码处理的实现
  • 图片处理的实现
  • 随机数处理的实现方式
  • 等等…

支持的语言

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jiT2gDwt-1625666538877)(https://i.loli.net/2021/06/18/3svjDucamYOJp8n.png)]

这个库目前支持的音频有 4 种语言:

  • 英文
  • 中文
  • 俄文
  • 日文

验证码默认参数

库中验证码的大小默认是 宽 240 px,高 80 px

在源码中的 image.go

const (
   // Standard width and height of a captcha image.
   StdWidth  = 240
   StdHeight = 80
   // Maximum absolute skew factor of a single digit.
   maxSkew = 0.7
   // Number of background circles.
   circleCount = 20
)
type Image struct {
   *image.Paletted
   numWidth  int
   numHeight int
   dotSize   int
   rng       siprng
}

随机数包含的字符

如下是验证码id中允许的字符 ,可以在源码中看到

源码包中的 random.go

// idChars are characters allowed in captcha id.
var idChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")

关于音频的处理

在源码的 sounds.go文件

目前对于音频只支持 4 种语言,"en", "ja", "ru", "zh".

/ NewAudio returns a new audio captcha with the given digits, where each digit
// must be in range 0-9. Digits are pronounced in the given language. If there
// are no sounds for the given language, English is used.
//
// Possible values for lang are "en", "ja", "ru", "zh".
func NewAudio(id string, digits []byte, lang string) *Audio {
   a := new(Audio)
   // Initialize PRNG.
   a.rng.Seed(deriveSeed(audioSeedPurpose, id, digits))
   if sounds, ok := digitSounds[lang]; ok {
      a.digitSounds = sounds
   } else {
      a.digitSounds = digitSounds["en"]
   }
   numsnd := make([][]byte, len(digits))
   nsdur := 0
   for i, n := range digits {
      snd := a.randomizedDigitSound(n)
      nsdur += len(snd)
      numsnd[i] = snd
   }
   // Random intervals between digits (including beginning).
   intervals := make([]int, len(digits)+1)
   intdur := 0
   for i := range intervals {
      dur := a.rng.Int(sampleRate, sampleRate*3) // 1 to 3 seconds
      intdur += dur
      intervals[i] = dur
   }
   // Generate background sound.
   bg := a.makeBackgroundSound(a.longestDigitSndLen()*len(digits) + intdur)
   // Create buffer and write audio to it.
   sil := makeSilence(sampleRate / 5)
   bufcap := 3*len(beepSound) + 2*len(sil) + len(bg) + len(endingBeepSound)
   a.body = bytes.NewBuffer(make([]byte, 0, bufcap))
   // Write prelude, three beeps.
   a.body.Write(beepSound)
   a.body.Write(sil)
   a.body.Write(beepSound)
   a.body.Write(sil)
   a.body.Write(beepSound)
   // Write digits.
   pos := intervals[0]
   for i, v := range numsnd {
      mixSound(bg[pos:], v)
      pos += len(v) + intervals[i+1]
   }
   a.body.Write(bg)
   // Write ending (one beep).
   a.body.Write(endingBeepSound)
   return a
}

其中关于语言的数据在digitSounds map 中

看到这个,就有点像做字库解析 和 嵌入式里面的数据解析了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bGRr0BH4-1625666538879)(https://i.loli.net/2021/06/19/A4mw2FGn6Itu8ch.png)]

var digitSounds = map[string][][]byte{
    "en": [][]byte{
        { // 0
            0x80, 0x7f, 0x80, 0x7f, 0x80, 0x80, 0x80, 0x7f, 0x80, 0x7f, 0x80,
            ...
        },
    "ru": [][]byte{
        { // 0
             0x7f, 0x7f, 0x7e, 0x7f, 0x7f, 0x7e, 0x7f, 0x7e, 0x7f, 0x7f, 0x7e,
            ...
        },
    "zh": [][]byte{
        { // 0
            0x7f, 0x80, 0x7f, 0x80, 0x80, 0x7f, 0x80, 0x7f, 0x80, 0x7f, 0x80,
            ...
        },
    "ja": [][]byte{
        { // 0
             0x7f, 0x80, 0x7f, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x83,
            ...
        },

开始案例的演示

my_captcha.html 实现如下

暂时关于音频的语言,就写了2种语言

  • 英文
  • 中文
<!doctype html>
<head>
    <title>GO 简单制作验证码案例</title>
    <style>
        input{
            margin-top: 30px;
        }
    </style>
</head>
<body>
<script>
    // 设置语言
    function setSrcQuery(e, q) {
        var src = e.src;
        var p = src.indexOf('?');
        if (p >= 0) {
            src = src.substr(0, p);
        }
        e.src = src + "?" + q
    }
    // 播放音频
    function playAudio() {
        var le = document.getElementById("lang");
        var lang = le.options[le.selectedIndex].value;
        var e = document.getElementById('audio')
        setSrcQuery(e, "lang=" + lang)
        e.style.display = 'block';
        e.autoplay = 'true';
        return false;
    }
    // 切换语言
    function changeLang() {
        var e = document.getElementById('audio')
        if (e.style.display == 'block') {
            playAudio();
        }
    }
    // 重新加载
    function reload() {
        setSrcQuery(document.getElementById('image'), "reload=" + (new Date()).getTime());
        setSrcQuery(document.getElementById('audio'), (new Date()).getTime());
        return false;
    }
</script>
<div align="center" >
    <select id="lang" onchange="changeLang()">
        <option value="en">英文</option>
        <option value="zh">中文</option>
    </select>
</div>
<form action="/processCapcha" method=post align="center">
    <p>请输入你在下面的图片中看到的数字:</p>
    <p><img id=image src="/captcha/{{.CaptchaId}}.png" alt="Captcha image"></p>
    <a href="#" onclick="reload()">重新加载</a>   |   <a href="#" onclick="playAudio()">播放音频验证码</a>
    <audio id=audio controls style="display:none" src="/captcha/{{.CaptchaId}}.wav" preload=none>
        You browser doesn't support audio.
        <a href="/captcha/download/{{.CaptchaId}}.wav">下载文件</a> to play it in the external player.
    </audio>
    <input type=hidden name=captchaId value="{{.CaptchaId}}" align=center><br>
    <input name=captchaSolution align=center>
    <input type=submit value=Submit>
</form>

main.go

  • 展示验证码
  • 处理验证码,结果展示
  • 重载验证码
  • 播放验证码音频
package main
import (
  "github.com/dchest/captcha"
  "io"
  "io/ioutil"
  "log"
  "net/http"
  "text/template"
)
const filePath = "./my_captcha.html"
// 读取 html 文件
func readHtml() string {
  var bytes []byte
  var err error
  if bytes, err = ioutil.ReadFile(filePath); err != nil {
    log.Fatalf("ioutil.ReadFile error filePath =  %s , err :"+filePath, err)
    return ""
  }
  return string(bytes)
}
// 读取html 文件,转成template.Template 指针
var formTemplate = template.Must(template.New("myCaptcha").Parse(readHtml()))
// 显示验证码
func showCaptcha(w http.ResponseWriter, r *http.Request) {
  if r.URL.Path != "/" {
    http.NotFound(w, r)
    return
  }
  d := struct {
    CaptchaId string
  }{
    captcha.New(),
  }
  // Execute将解析后的模板应用到指定的数据对象,并将输出写入wr
  if err := formTemplate.Execute(w, &d); err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
}
// 处理验证码,跳转结果页面
func resultPage(w http.ResponseWriter, r *http.Request) {
  w.Header().Set("Content-Type", "text/html; charset=utf-8")
  if !captcha.VerifyString(r.FormValue("captchaId"), r.FormValue("captchaSolution")) {
    io.WriteString(w, "错误的验证码,请重新输入\n")
  } else {
    io.WriteString(w, "验证吗正确,你很棒哦!!\n")
  }
  io.WriteString(w, "<br><a href='/'>再试一下</a>")
}
func main() {
  // 简单设置log参数
  log.SetFlags(log.Lshortfile | log.LstdFlags)
  http.HandleFunc("/", showCaptcha)
  http.HandleFunc("/processCapcha", resultPage)
  http.Handle("/captcha/", captcha.Server(captcha.StdWidth, captcha.StdHeight))
  log.Println("starting server : 8888")
  if err := http.ListenAndServe("localhost:8888", nil); err != nil {
    log.Fatal(err)
  }
}

上述代码的宽高 是这样的

StdWidth  = 240
StdHeight = 80

上述 HandleFunc 的回调函数是这个样子的,之前介绍 gin 的时候有分享过, 可以回头看看 文章 来我们一起探究一下net/http 的代码流程

// HandleFunc registers the handler function for the given pattern
  // in the DefaultServeMux.
  // The documentation for ServeMux explains how patterns are matched.
  func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)
  }

验证码实际效果

点击播放音频验证码的时候,可以看到这样的效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oZDXadhF-1625666538884)(https://i.loli.net/2021/06/19/HapmAjkCbiXRz28.png)]

该音频,会根据我们选择语言,来播放不同的语音,读取图片上的数字

总结

  • 验证码种类梳理
  • 验证码库的安装
  • 验证码库的源码介绍
  • 实操,编码
  • 验证码效果展示

欢迎点赞,关注,收藏

朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力

好了,本次就到这里,下一次 如何使用GOLANG发送邮件

技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。

我是小魔童哪吒,欢迎点赞关注收藏,下次见~

相关文章
|
2天前
|
缓存 NoSQL 前端开发
一文搞懂Go整合captcha实现验证码功能
一文搞懂Go整合captcha实现验证码功能
30 0
|
10月前
|
Shell Go C++
我们用 GO 玩一下验证码
嗨,我是小魔童哪吒,咱们上次分享的GO 中 defer 的实现原理,再来回顾一下吧
|
存储 人工智能 前端开发
彩虹女神跃长空,Go语言进阶之Go语言高性能Web框架Iris项目实战-登录与图形验证码(captcha)EP06
书接上回,上一回我们按照“低耦合高内聚”的组织架构方针对项目的整体结构进行了优化,本回将会继续编写业务,那就是用户的登录逻辑,将之前用户管理模块中添加的用户账号进行账号和密码的校验,校验通过后留存当前登录用户的信息,过程中使用图形验证码强制进行人机交互,防止账号的密码被暴力破解。
彩虹女神跃长空,Go语言进阶之Go语言高性能Web框架Iris项目实战-登录与图形验证码(captcha)EP06
|
存储 缓存 NoSQL
一文搞懂Go整合captcha实现验证码功能
一文搞懂Go整合captcha实现验证码功能
|
21小时前
|
JSON 前端开发 Go
lucky - go 语言实现的快速开发平台
go 语言实现的快速开发平台,自动生成crud代码,前端页面通过json配置,无需编写前端代码。
6 0
|
1天前
|
存储 Java Go
Go 语言切片如何扩容?(全面解析原理和过程)
Go 语言切片如何扩容?(全面解析原理和过程)
12 2
|
2天前
|
负载均衡 Go 调度
使用Go语言构建高性能的Web服务器:协程与Channel的深度解析
在追求高性能Web服务的今天,Go语言以其强大的并发性能和简洁的语法赢得了开发者的青睐。本文将深入探讨Go语言在构建高性能Web服务器方面的应用,特别是协程(goroutine)和通道(channel)这两个核心概念。我们将通过示例代码,展示如何利用协程处理并发请求,并通过通道实现协程间的通信和同步,从而构建出高效、稳定的Web服务器。
|
2天前
|
算法 Go 分布式数据库
构建高可用的分布式数据库集群:使用Go语言与Raft共识算法
随着数据量的爆炸式增长,单一数据库服务器已难以满足高可用性和可扩展性的需求。在本文中,我们将探讨如何使用Go语言结合Raft共识算法来构建一个高可用的分布式数据库集群。我们不仅会介绍Raft算法的基本原理,还会详细阐述如何利用Go语言的并发特性和网络编程能力来实现这一目标。此外,我们还将分析构建过程中可能遇到的挑战和解决方案,为读者提供一个完整的实践指南。
|
2天前
|
消息中间件 Go API
基于Go语言的微服务架构实践
随着云计算和容器化技术的兴起,微服务架构成为了现代软件开发的主流趋势。Go语言,以其高效的性能、简洁的语法和强大的并发处理能力,成为了构建微服务应用的理想选择。本文将探讨基于Go语言的微服务架构实践,包括微服务的设计原则、服务间的通信机制、以及Go语言在微服务架构中的优势和应用案例。
|
2天前
|
安全 测试技术 数据库连接
使用Go语言进行并发编程
【5月更文挑战第15天】Go语言以其简洁语法和强大的并发原语(goroutines、channels)成为并发编程的理想选择。Goroutines是轻量级线程,由Go运行时管理。Channels作为goroutine间的通信机制,确保安全的数据交换。在编写并发程序时,应遵循如通过通信共享内存、使用`sync`包同步、避免全局变量等最佳实践。理解并发与并行的区别,有效管理goroutine生命周期,并编写测试用例以确保代码的正确性,都是成功进行Go语言并发编程的关键。