美文网首页故事简友广场想法
我们用GO玩一下验证码

我们用GO玩一下验证码

作者: 阿兵云原生 | 来源:发表于2021-06-21 07:35 被阅读0次

    我们用GO玩一下验证码

    我们用GO玩一下验证码.jpg

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

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

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

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

    [图片上传失败...(image-9cf50e-1624232076373)]

    GO 的验证码介绍

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

    • 传统输入的形式

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

    • 输入类型的图形验证码

    这个主要是来打广告的

    • 纯行为验证码

    例如,按照提示滑动等等

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

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

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

    例如某宝的验证码

    • 智能验证码

    例如,点触智能验证码

    GO 验证码案例

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

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

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

    [图片上传失败...(image-4a94f-1624232076373)]

    安装captcha

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

    go get github.com/dchest/captcha
    

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

    源码目录

    [图片上传失败...(image-dccb9e-1624232076373)]

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

    支持的语言

    [图片上传失败...(image-6e0ed1-1624232076373)]

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

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

    [图片上传失败...(image-9b4b14-1624232076373)]

    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,
                ...
            }, 
    

    开始案例的演示

    [图片上传失败...(image-78d05d-1624232076373)]

    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)
        }
    

    验证码实际效果

    [图片上传失败...(image-3b95b5-1624232076373)]

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

    [图片上传失败...(image-b889d1-1624232076373)]

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

    [图片上传失败...(image-749c33-1624232076373)]

    总结

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

    欢迎点赞,关注,收藏

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

    [图片上传失败...(image-66f48f-1624232076373)]

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

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

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

    相关文章

      网友评论

        本文标题:我们用GO玩一下验证码

        本文链接:https://www.haomeiwen.com/subject/cyweyltx.html