咱們用GO玩一下驗證碼

[TOC]html

咱們用GO玩一下驗證碼

嗨,我是小魔童哪吒,我們上次分享的GO 中 defer 的實現原理,再來回顧一下吧git

  • 分享了defer是什麼
  • 簡單示意了棧和隊列
  • defer的數據結構和實現原理,具體的源碼展現
  • GO 中defer3 條規則

要是對 GO 中 defer 實現原理還有點興趣的話,歡迎查看文章 GO 中 defer的實現原理github

今天咱們來分享一些使用 GO 實現小案例,我們邊玩邊成長shell

GO 的驗證碼介紹

咱們平時使用到的驗證碼大體分爲這幾種,我們梳理一下:數據結構

  • 傳統輸入的形式

輸入圖片上的數字,文字,字母等等dom

  • 輸入類型的圖形驗證碼

這個主要是來打廣告的函數

  • 純行爲驗證碼

例如,按照提示滑動等等post

  • 圖標選擇與行爲輔助的驗證碼

例如我們買火車票的時候驗證碼,各類圖標讓你選學習

  • 點擊式的圖文驗證與行爲輔助

例如某寶的驗證碼編碼

  • 智能驗證碼

例如,點觸智能驗證碼

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

驗證碼實際效果

點擊播放音頻驗證碼的時候,能夠看到這樣的效果

該音頻,會根據咱們選擇語言,來播放不一樣的語音,讀取圖片上的數字

總結

  • 驗證碼種類梳理
  • 驗證碼庫的安裝
  • 驗證碼庫的源碼介紹
  • 實操,編碼
  • 驗證碼效果展現

歡迎點贊,關注,收藏

朋友們,你的支持和鼓勵,是我堅持分享,提升質量的動力

好了,本次就到這裏,下一次 如何使用GOLANG發送郵件

技術是開放的,咱們的心態,更應是開放的。擁抱變化,向陽而生,努力向前行。

我是小魔童哪吒,歡迎點贊關注收藏,下次見~

相關文章
相關標籤/搜索