go实现验证码

Go进阶37:重构我的base64Captcha图形验证码项目

Go进阶37:重构我的base64Captcha图形验证码项目

Go Report CardGoDocBuild Statuscodecovstability-stableFoundation

🎃Base64captcha🎃 几行代码就可以定义自己内容的图形验证码库,支持任意unicode字符的内容.

1. 📒 文档&Demo 📒

2. 🚀 快速上手 🚀

2.1 📥 下载base64Captcha包 📥

go get -u github.com/mojocn/base64Captcha

2.2 🏂 在您的项目中使用base64Captcha 🏂

2.2.1 🏇 实现Store interface 或者使用自带memory store 🏇

type Store interface {
	// Set sets the digits for the captcha id.
	Set(id string, value string)

	// Get returns stored digits for the captcha id. Clear indicates
	// whether the captcha must be deleted from the store.
	Get(id string, clear bool) string
	
    //Verify captcha's answer directly
	Verify(id, answer string, clear bool) bool
}

2.2.2 🏄 实现Driver interface 或者使用自带 drivers 🏄

包自带driver:

  1. Driver Digit
  2. Driver String
  3. Driver Math
  4. Driver Chinese
// Driver captcha interface for captcha engine to to write staff
type Driver interface {
	//DrawCaptcha draws binary item
	DrawCaptcha(content string) (item Item, err error)
	//GenerateIdQuestionAnswer creates rand id, content and answer
	GenerateIdQuestionAnswer() (id, q, a string)
}

2.2.3 🚴 核心代码captcha.go 🚴

captcha.go 是package的入口文件,源代码逻辑非常简单,如下:

func init() {
	//init rand seed
	rand.Seed(time.Now().UnixNano())
}

// Captcha captcha basic information.
type Captcha struct {
	Driver Driver
	Store  Store
}

func NewCaptcha(driver Driver, store Store) *Captcha {
	return &Captcha{Driver: driver, Store: store}
}

//Generate generates a random id, base64 image string or an error if any
func (c *Captcha) Generate() (id, b64s string, err error) {
	id,content, answer := c.Driver.GenerateIdQuestionAnswer()
	item, err := c.Driver.DrawCaptcha(content)
	if err != nil {
		return "", "", err
	}
	c.Store.Set(id, answer)
	b64s = item.EncodeB64string()
	return
}
//if you has multiple captcha instances which shares a same store. You may want to use `store.Verify` method instead.
//Verify by given id key and remove the captcha value in store, return boolean value.
func (c *Captcha) Verify(id, answer string, clear bool) (match bool) {
	match = c.Store.Get(id, clear) == answer
	return
}

2.2.4 🚵 生成Base64(image/audio)验证码字符串 🚵

//Generate generates a random id, base64 image string or an error if any
func (c *Captcha) Generate() (id, b64s string, err error) {
	id,content, answer := c.Driver.GenerateIdQuestionAnswer()
	item, err := c.Driver.DrawCaptcha(content)
	if err != nil {
		return "", "", err
	}
	c.Store.Set(id, answer)
	b64s = item.EncodeB64string()
	return
}

2.2.5 🤸 校验验证码内容 🤸

//if you has multiple captcha instances which shares a same store. You may want to use `store.Verify` method instead.
//Verify by given id key and remove the captcha value in store, return boolean value.
func (c *Captcha) Verify(id, answer string, clear bool) (match bool) {
	match = c.Store.Get(id, clear) == answer
	return
}

2.2.6 🏃 完整实例代码 🏃

// example of HTTP server that uses the captcha package.
package main

import (
	"encoding/json"
	"fmt"
	"github.com/mojocn/base64Captcha"
	"log"
	"net/http"
)

//configJsonBody json request body.
type configJsonBody struct {
	Id            string
	CaptchaType   string
	VerifyValue   string
	DriverAudio   *base64Captcha.DriverAudio
	DriverString  *base64Captcha.DriverString
	DriverChinese *base64Captcha.DriverChinese
	DriverMath    *base64Captcha.DriverMath
	DriverDigit   *base64Captcha.DriverDigit
}

var store = base64Captcha.DefaultMemStore

// base64Captcha create http handler
func generateCaptchaHandler(w http.ResponseWriter, r *http.Request) {
	//parse request parameters
	decoder := json.NewDecoder(r.Body)
	var param configJsonBody
	err := decoder.Decode(&param)
	if err != nil {
		log.Println(err)
	}
	defer r.Body.Close()
	var driver base64Captcha.Driver

	//create base64 encoding captcha
	switch param.CaptchaType {
	case "audio":
		driver = param.DriverAudio
	case "string":
		driver = param.DriverString.ConvertFonts()
	case "math":
		driver = param.DriverMath.ConvertFonts()
	case "chinese":
		driver = param.DriverChinese.ConvertFonts()
	default:
		driver = param.DriverDigit
	}
	c := base64Captcha.NewCaptcha(driver, store)
	id, b64s, err := c.Generate()
	body := map[string]interface{}{"code": 1, "data": b64s, "captchaId": id, "msg": "success"}
	if err != nil {
		body = map[string]interface{}{"code": 0, "msg": err.Error()}
	}
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	json.NewEncoder(w).Encode(body)
}

// base64Captcha verify http handler
func captchaVerifyHandle(w http.ResponseWriter, r *http.Request) {

	//parse request json body
	decoder := json.NewDecoder(r.Body)
	var param configJsonBody
	err := decoder.Decode(&param)
	if err != nil {
		log.Println(err)
	}
	defer r.Body.Close()
	//verify the captcha
	body := map[string]interface{}{"code": 0, "msg": "failed"}
	if store.Verify(param.Id, param.VerifyValue, true) {
		body = map[string]interface{}{"code": 1, "msg": "ok"}
	}
	//set json response
	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	json.NewEncoder(w).Encode(body)
}

//start a net/http server
func main() {
	//serve Vuejs+ElementUI+Axios Web Application
	http.Handle("/", http.FileServer(http.Dir("./static")))

	//api for create captcha
	http.HandleFunc("/api/getCaptcha", generateCaptchaHandler)

	//api for verify captcha
	http.HandleFunc("/api/verifyCaptcha", captchaVerifyHandle)

	fmt.Println("Server is at :8777")
	if err := http.ListenAndServe(":8777", nil); err != nil {
		log.Fatal(err)
	}
}

2.3 🎬 使用历史版本 🎬

v1.2.2

` go get github.com/mojocn/base64Captcha@v1.2.2 `

3. 🎨 定制自己的图形验证码 🎨

您那个定制自己的图形验码内容,只需实现 interface driver 和 interface item.

下面是几个可以参考的driver实现示例:

  1. DriverMath
  2. DriverChinese
  3. ItemChar

您甚至可以设计captcha struct成您想要的功能

4. 💖 致谢 💖

5. 🍭 Licence 🍭

base64Captcha source code is licensed under the Apache Licence, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html).

类别和标签

 
8 条评论
未登录用户
@qiaocco
 
qiaocco发表于大约 2 年前
type memoryStore struct {
	sync.RWMutex
	digitsById map[string]string
	idByTime   *list.List
	// Number of items stored since last collection.
	numStored int
	// Number of saved items that triggers collection.
	collectNum int
	// Expiration time of captchas.
	expiration time.Duration
}

idByTime为什么设置成*list.List类型,有什么好处?

@mojocn
 
mojocn发表于大约 2 年前

链表 快速遍历

@Meng-Xin
 
Meng-Xin发表于将近 2 年前

不怎么会用,我这样写,报错了

package tool

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/mojocn/base64Captcha"
)


type CaptchaResult struct {
	Id string `json:"id"`
	Base64Blob	string `json:"base_64_blob"`
	VertifyValue string `json:"vertify_value"`
}
type configJsonBody struct {
	Id            string
	CaptchaType   string
	VerifyValue   string
	DriverAudio   *base64Captcha.DriverAudio
	DriverString  *base64Captcha.DriverString
	DriverChinese *base64Captcha.DriverChinese
	DriverMath    *base64Captcha.DriverMath
	DriverDigit   *base64Captcha.DriverDigit
}

var store = base64Captcha.DefaultMemStore

//生成图形验证码
func GenerateCaptcha(ctx *gin.Context)  {
	//配置项 driver ,param
	var driver base64Captcha.Driver
	var param configJsonBody
	driver = param.DriverString.ConvertFonts()

	//生成图形验证码
	c := base64Captcha.NewCaptcha(driver, store)
	captchaId, b64s, err := c.Generate()
	if err != nil {
		fmt.Println("图形验证码生成失败")
		Failed(ctx,"图形验证码生成失败")
		return
	}
	captchaResult := CaptchaResult{Id:captchaId,Base64Blob: b64s}
	Success(ctx,map[string]interface{}{
		"captcha_result" : captchaResult,
	})
}
@Meng-Xin
 
Meng-Xin发表于将近 2 年前

请问取消了 configCharacter 这个配置方法以后,怎么自定义一个验证码呢?

@mojocn
 
mojocn发表于将近 2 年前

@Meng-Xin
请问取消了 configCharacter 这个配置方法以后,怎么自定义一个验证码呢?

driver = param.DriverString.ConvertFonts() 中的 全部字段填充就可以了

@BuxsRen
 
BuxsRen发表于超过 1 年前

完整实例运行直接报:
runtime error: invalid memory address or nil pointer dereference

@GuoXiaoBo
 
GuoXiaoBo发表于超过 1 年前

https://github.com/mojocn/base64Captcha/blob/develop/interface_store.go

最新的1.3.5为什么Get&Verify 仍然不返回error呢,redis store情况下不太友好吧~

@xiwang007
 
xiwang007发表于超过 1 年前

作者给的demo需要一点web基础,我这边直接把web相关的抽离了,只剩下验证码的生成和验证给大家交流学习.

创建一个mycaptcha_test.go的文件

然后运行里面TestMyCaptcha单元测试函数

package mycaptcha

import (
	"log"
	"testing"

	"github.com/mojocn/base64Captcha"
)

//configJsonBody json request body.
type configJsonBody struct {
	Id            string
	CaptchaType   string
	VerifyValue   string
	DriverAudio   *base64Captcha.DriverAudio
	DriverString  *base64Captcha.DriverString
	DriverChinese *base64Captcha.DriverChinese
	DriverMath    *base64Captcha.DriverMath
	DriverDigit   *base64Captcha.DriverDigit
}

var store = base64Captcha.DefaultMemStore

// base64Captcha create return id, b64s, err
func GetCaptcha() (string, string, error) {

	// 	{
	// 		ShowLineOptions: [],
	// 		CaptchaType: "string",
	// 		Id: '',
	// 		VerifyValue: '',
	// 		DriverAudio: {
	// 			Length: 6,
	// 			Language: 'zh'
	// 		},
	// 		DriverString: {
	// 			Height: 60,
	// 			Width: 240,
	// 			ShowLineOptions: 0,
	// 			NoiseCount: 0,
	// 			Source: "1234567890qwertyuioplkjhgfdsazxcvbnm",
	// 			Length: 6,
	// 			Fonts: ["wqy-microhei.ttc"],
	// 			BgColor: {R: 0, G: 0, B: 0, A: 0},
	// 		},
	// 		DriverMath: {
	// 			Height: 60,
	// 			Width: 240,
	// 			ShowLineOptions: 0,
	// 			NoiseCount: 0,
	// 			Length: 6,
	// 			Fonts: ["wqy-microhei.ttc"],
	// 			BgColor: {R: 0, G: 0, B: 0, A: 0},
	// 		},
	// 		DriverChinese: {
	// 			Height: 60,
	// 			Width: 320,
	// 			ShowLineOptions: 0,
	// 			NoiseCount: 0,
	// 			Source: "设想,你在,处理,消费者,的音,频输,出音,频可,能无,论什,么都,没有,任何,输出,或者,它可,能是,单声道,立体声,或是,环绕立,体声的,,不想要,的值",
	// 			Length: 2,
	// 			Fonts: ["wqy-microhei.ttc"],
	// 			BgColor: {R: 125, G: 125, B: 0, A: 118},
	// 		},
	// 		DriverDigit: {
	// 			Height: 80,
	// 			Width: 240,
	// 			Length: 5,
	// 			MaxSkew: 0.7,
	// 			DotCount: 80
	// 		}
	// 	},
	// 	blob: "",
	// 	loading: false
	// }

	// https://captcha.mojotv.cn/ 调试配置
	var param configJsonBody = configJsonBody{
		Id:          "",
		CaptchaType: "string",
		VerifyValue: "",
		DriverAudio: &base64Captcha.DriverAudio{},
		DriverString: &base64Captcha.DriverString{
			Length:          4,
			Height:          60,
			Width:           240,
			ShowLineOptions: 2,
			NoiseCount:      0,
			Source:          "1234567890qwertyuioplkjhgfdsazxcvbnm",
		},
		DriverChinese: &base64Captcha.DriverChinese{},
		DriverMath:    &base64Captcha.DriverMath{},
		DriverDigit:   &base64Captcha.DriverDigit{},
	}
	var driver base64Captcha.Driver

	//create base64 encoding captcha
	switch param.CaptchaType {
	case "audio":
		driver = param.DriverAudio
	case "string":
		driver = param.DriverString.ConvertFonts()
	case "math":
		driver = param.DriverMath.ConvertFonts()
	case "chinese":
		driver = param.DriverChinese.ConvertFonts()
	default:
		driver = param.DriverDigit
	}
	c := base64Captcha.NewCaptcha(driver, store)
	return c.Generate()
	// id, b64s, err := c.Generate()

	// body := map[string]interface{}{"code": 1, "data": b64s, "captchaId": id, "msg": "success"}
	// if err != nil {
	// 	body = map[string]interface{}{"code": 0, "msg": err.Error()}
	// }
	// var _ = body

	// // log.Println(body)
	// log.Println(1)
	// log.Println(id)

	// log.Printf("store =%+v\n", store)
}

// base64Captcha verify
func VerifyCaptcha(id, VerifyValue string) bool {
	return store.Verify(id, VerifyValue, true)
}

func TestMyCaptcha(t *testing.T) {
	id, b64s, err := GetCaptcha()
	if err != nil {
		return
	}
	var _ = b64s
	log.Println("id =", id)
	log.Println("VerifyValue =", store.Get(id, true))
	result := VerifyCaptcha(id, store.Get(id, true))
	log.Println("result =", result)
}

https://mojotv.cn/go/refactor-base64-captcha

posted @ 2023-08-18 21:21  技术颜良  阅读(58)  评论(0编辑  收藏  举报