[TOC]

咱们用GO玩一下验证码

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

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

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

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

GO 的验证码介绍

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

  • 传统输出的模式

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

  • 输出类型的图形验证码

这个次要是来打广告的

  • 纯行为验证码

例如,依照提醒滑动等等

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

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

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

例如某宝的验证码

  • 智能验证码

例如,点触智能验证码

GO 验证码案例

咱们明天就来玩一玩第一种,应用最多的一种验证码吧

会应用 GO 的这个验证码库来实现,github.com/dchest/captcha

若咱们向C/C++一样,将很多的底层解决都是咱们本人来封装来实现的话,那还是挺累人的,GO 这一点的确蛮好,有很多应用的包,咱们在应用之余,也能够站在伟人的肩膀,学习源码中的实现形式,学习大佬们的设计思维。

装置captcha

大家应用如下命令就能够下载下来应用

go get github.com/dchest/captcha

当咱们在GOLAND 中用到 captcha库的时候,咱们能够看看源码目录

源码目录

  • 有源码的具体应用案例
  • 具体的示例图片
  • 相干音频解决的实现
  • 验证码解决的实现
  • 图片解决的实现
  • 随机数解决的实现形式
  • 等等...

反对的语言

这个库目前反对的音频有 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 中

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

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 mainimport (    "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  = 240StdHeight = 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)    }

验证码实际效果

点击播放音频验证码的时候,能够看到这样的成果

该音频,会依据咱们抉择语言,来播放不同的语音,读取图片上的数字

总结

  • 验证码品种梳理
  • 验证码库的装置
  • 验证码库的源码介绍
  • 实操,编码
  • 验证码成果展现

欢送点赞,关注,珍藏

敌人们,你的反对和激励,是我保持分享,提高质量的能源

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

技术是凋谢的,咱们的心态,更应是凋谢的。拥抱变动,背阴而生,致力向前行。

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