go 集成base64Captcha 支持多种验证码
base64Captcha
是一个基于 Go 语言开发的验证码生成库,主要用于在 Web 应用中集成验证码功能,以增强系统的安全性。以下是其主要特点和简介:
base64Captcha主要功能
- 验证码类型丰富:支持生成多种类型的验证码,包括纯数字、纯字母、数字与字母组合、数学公式、汉字、音频等,满足不同场景的需求。
- 多种存储方式:
- 内存存储:简单易用,低延迟,无网络开销,但无法进行数据持久化,扩展性较差,适合单机部署。
- Redis 存储:性能高,支持分布式存储,但会增加网络开销,同时具有一定的复杂度,适合多台服务器部署的场景。
- 自定义配置灵活:
- 验证码驱动:可以通过实现
Driver
接口来自定义验证码的生成逻辑,也可以使用库自带的多种驱动,如DriverString
、DriverMath
、DriverChinese
、DriverAudio
、DriverDigit
等。 - 验证码样式:支持自定义验证码的高度、宽度、噪点数量、干扰线数量、验证码长度、字符源、背景色、字体等样式参数。
- 验证码驱动:可以通过实现
- 返回格式便捷:以 base64 编码方式返回验证码图片,方便前端展示。
使用示例
以下是一个使用 base64Captcha
生成和验证验证码的简单示例:
后端代码
// example of HTTP server that uses the captcha package.
package mainimport ("encoding/json""fmt""github.com/mojocn/base64Captcha""log""net/http""time"
)// configJsonBody json request body.
type configJsonBody struct {Id stringCaptchaType stringVerifyValue stringDriverAudio *base64Captcha.DriverAudioDriverString *base64Captcha.DriverStringDriverChinese *base64Captcha.DriverChineseDriverMath *base64Captcha.DriverMathDriverDigit *base64Captcha.DriverDigit
}// var store = base64Captcha.DefaultMemStore
var store = base64Captcha.NewMemoryStore(1000, time.Minute*2)// base64Captcha create http handler
func generateCaptchaHandler(w http.ResponseWriter, r *http.Request) {//parse request parametersdecoder := json.NewDecoder(r.Body)var param configJsonBodyerr := decoder.Decode(¶m)if err != nil {log.Println(err)}defer r.Body.Close()var driver base64Captcha.Driver//choose driverswitch param.CaptchaType {case "audio":driver = param.DriverAudiocase "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 parametersdecoder := json.NewDecoder(r.Body)var param configJsonBodyerr := decoder.Decode(¶m)if err != nil {log.Println(err)}defer r.Body.Close()//verify the captchabody := 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 responsew.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 Applicationhttp.Handle("/", http.FileServer(http.Dir("./static")))//api for create captchahttp.HandleFunc("/api/getCaptcha", generateCaptchaHandler)//api for verify captchahttp.HandleFunc("/api/verifyCaptcha", captchaVerifyHandle)fmt.Println("Server is at :9599")if err := http.ListenAndServe(":9599", nil); err != nil {log.Fatal(err)}
}
前端页面
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>Config Parameter Playground</title><meta name="Keywords" content="golang,godoc,captcha,base64,png,图像验证码"/><meta name="Description" content="Base64 Captcha"/><link rel="stylesheet" href="https://cdn.bootcss.com/element-ui/2.0.11/theme-chalk/index.css"><style>.el-header, .el-footer {background-color: #B3C0D1;color: #333;text-align: center;line-height: 0px;}.el-header > p {margin-top: 12px !important;}.el-main {background-color: #E9EEF3;color: #333;text-align: center;/*line-height: 160px;*/}body {margin: 0px;text-align: center;}.login-container {-webkit-border-radius: 5px;border-radius: 5px;-moz-border-radius: 5px;background-clip: padding-box;margin: 15px auto auto auto;width: 480px;padding: 6px;background: #fff;border: 1px solid #eaeaea;box-shadow: 0 0 25px #cac6c6;}.title {margin: 0px auto 20px auto;text-align: center;color: #505458;}.captcha-img {cursor: pointer;position: relative;border: 1px solid chartreuse;box-shadow: 0 0 6px #cac6c6;}.el-form-item {margin-bottom: 0px;}.el-main {background-color: #E9EEF3;color: #333;text-align: center;padding: 0px !important;}</style><!-- Place this tag in your head or just before your close body tag. --><script src="https://buttons.github.io/buttons.js"></script><script src="https://cdn.bootcss.com/vue/2.5.13/vue.min.js"></script><script src="https://cdn.bootcss.com/element-ui/2.0.11/index.js"></script><script src="https://cdn.bootcss.com/axios/0.17.1/axios.min.js"></script>
</head>
<body>
<div id="app"><el-container><el-headerstyle="height: 90px!important;"><!-- Place this tag where you want the button to render. --><p><a class="github-button" href="https://github.com/mojocn/base64captcha" data-size="large"data-show-count="true" aria-label="Star mojocn/base64captcha on GitHub">Star</a><!-- Place this tag where you want the button to render. --><a class="github-button" href="https://github.com/mojocn" data-size="large" data-show-count="true"aria-label="Follow @mojocn on GitHub">Follow @mojocn</a><a class="github-button" href="https://github.com/JJJJJJJerk" data-size="large" data-show-count="true"aria-label="Follow @mojocn on GitHub">Follow @Eric Zhou</a><!-- Place this tag where you want the button to render. --><a class="github-button" href="https://github.com/mojocn/base64captcha/issues" data-size="large"data-show-count="true" aria-label="Issue mojocn/base64captcha on GitHub">Issue</a><!-- Place this tag where you want the button to render. --><a class="github-button" href="https://github.com/mojocn/base64captcha/archive/master.zip"data-size="large" aria-label="Download mojocn/base64captcha on GitHub">Download</a></p><a href="https://godoc.org/github.com/mojocn/base64Captcha" rel="nofollow"><imgsrc="https://camo.githubusercontent.com/600bdcf87a3b63b5300c6673401901196360a82a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f6d6f6a6f636e2f626173653634436170746368613f7374617475732e737667"alt="GoDoc" data-canonical-src="https://godoc.org/github.com/mojocn/base64Captcha?status.svg"style="max-width:100%;"></a><a href="https://goreportcard.com/report/github.com/mojocn/base64Captcha" rel="nofollow"><imgsrc="https://camo.githubusercontent.com/0848346ead4693b8b2d975d8cbbb032945fb708d/68747470733a2f2f676f7265706f7274636172642e636f6d2f62616467652f6769746875622e636f6d2f6d6f6a6f636e2f62617365363443617074636861"alt="Go Report Card"data-canonical-src="https://goreportcard.com/badge/github.com/mojocn/base64Captcha"style="max-width:100%;"></a><a href="http://golangfoundation.org" rel="nofollow"><imgsrc="https://camo.githubusercontent.com/36f4996a1c92724272c100659936593ff0909a29/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f476f6c616e672d466f756e646174696f6e2d677265656e2e737667"alt="Foundation" data-canonical-src="https://img.shields.io/badge/Golang-Foundation-green.svg"style="max-width:100%;"></a><a href="https://codecov.io/gh/mojocn/base64Captcha"><img src="https://codecov.io/gh/mojocn/base64Captcha/branch/master/graph/badge.svg"/></a><a href="http://doge.mit-license.org"><imgsrc="https://camo.githubusercontent.com/3d7aa1ddbfa86368152bf42123c17b69ea8070be/687474703a2f2f696d672e736869656c64732e696f2f3a6c6963656e73652d6d69742d626c75652e737667"alt="License" data-canonical-src="http://img.shields.io/:license-mit-blue.svg"style="max-width:100%;"></a><a href="https://camo.githubusercontent.com/69f50fbca17d6577018651ff9afcb55cdac03bc4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f73746162696c6974792d737461626c652d627269676874677265656e2e737667"target="_blank"><imgsrc="https://camo.githubusercontent.com/69f50fbca17d6577018651ff9afcb55cdac03bc4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f73746162696c6974792d737461626c652d627269676874677265656e2e737667"alt="stability-stable"data-canonical-src="https://img.shields.io/badge/stability-stable-brightgreen.svg"style="max-width:100%;"></a></p></el-header><el-main><el-row style="width: 50%;margin:8px auto;background: #9e9e9e;padding: 8px 2rem" type="flex"justify="center" align="middle"><el-col :span="16"><img @click.prevent="generateCaptcha" :src="blob" class="captcha-img"v-if="form.CaptchaType !== 'audio'"/><audio controls :src="blob" autoplay v-if="form.CaptchaType === 'audio'"/></el-col><el-col :span="8"><el-form><el-form-item><el-inputtype="text"v-model="form.VerifyValue"auto-complete="off"style="margin: 0 auto 8px auto"placeholder="input your captcha numbers"></el-input></el-form-item><el-form-item><el-buttontype="primary"style="width:100%"v-loading="loading"@click.native.prevent="submitForm">Verify Captcha</el-button></el-form-item></el-form></el-col></el-row><el-tabs v-model="form.CaptchaType"style="width: 70%;margin-left: auto;margin-right: auto;"type="border-card" @tab-click="handleClick"><el-tab-pane label="DriverDigit" name="digit"><el-formlabel-width="280px"label-position="left"><el-form-item label="DriverDigit.Length"><el-slider v-model="form.DriverDigit.Length" :min="1" :max="10" show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="DriverDigit.Width"><el-slider v-model="form.DriverDigit.Width" :min="20" :max="480" :step="5"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="DriverDigit.Height"><el-slider v-model="form.DriverDigit.Height" :min="20" :max="180" :step="5"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="DriverDigit.MaxSkew"><el-slider v-model="form.DriverDigit.MaxSkew" :step="0.05" :min="0.1" :max="1"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="DriverDigit.DotCount"><el-slider v-model="form.DriverDigit.DotCount" :min="2" :max="100" show-input@change="generateCaptcha"></el-slider></el-form-item></el-form></el-tab-pane><el-tab-pane label="DriverString" name="string"><el-formlabel-width="280px"label-position="left"><el-form-item label="DriverString.Length"><el-slider v-model="form.DriverString.Length" :min="1" :max="10" show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="DriverString.Source"><el-input v-model.trim="form.DriverString.Source"type="textarea"placeholder="Any Unicode string is OK, Korean Japanese Greek Arabic Thai ..."@change="generateCaptcha"></el-input></el-form-item><el-form-item label="DriverString.Width"><el-slider v-model="form.DriverString.Width" :min="20" :max="480" :step="5"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="DriverString.Height"><el-slider v-model="form.DriverString.Height" :min="20" :max="180" :step="5"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="DriverString.NoiseCount"><el-slider v-model="form.DriverString.NoiseCount" :min="0" :max="480" :step="5"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="DriverString.ShowLineOptions"><el-checkbox-group v-model="form.ShowLineOptions" @change="generateCaptcha"><el-checkbox label="2">OptionShowHollowLine</el-checkbox><el-checkbox label="4">OptionShowSlimeLine</el-checkbox><el-checkbox label="8">OptionShowSineLine</el-checkbox></el-checkbox-group></el-form-item><el-form-item label="DriverString.Fonts"><el-checkbox-group v-model="form.DriverString.Fonts" @change="generateCaptcha"><el-checkbox v-for="f in fonts" :label="f" :key="f">{{f}}</el-checkbox></el-checkbox-group></el-form-item><el-form-item label="DriverString.BgColor."><el-form-item label="R"><el-slider v-model="form.DriverString.BgColor.R" :min="0" :max="254" :step="1"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="G"><el-slider v-model="form.DriverString.BgColor.G" :min="0" :max="254" :step="1"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="B"><el-slider v-model="form.DriverString.BgColor.B" :min="0" :max="254" :step="1"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="A"><el-slider v-model="form.DriverString.BgColor.A" :min="0" :max="254" :step="1"show-input@change="generateCaptcha"></el-slider></el-form-item></el-form-item></el-form></el-tab-pane><el-tab-pane label="DriverMath" name="math"><el-formlabel-width="280px"label-position="left"><el-form-item label="DriverMath.Width"><el-slider v-model="form.DriverMath.Width" :min="20" :max="480" :step="5"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="DriverMath.Height"><el-slider v-model="form.DriverMath.Height" :min="20" :max="180" :step="5"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="DriverMath.NoiseCount"><el-slider v-model="form.DriverMath.NoiseCount" :min="0" :max="480" :step="5"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="DriverMath.ShowLineOptions"><el-checkbox-group v-model="form.ShowLineOptions" @change="generateCaptcha"><el-checkbox label="2">OptionShowHollowLine</el-checkbox><el-checkbox label="4">OptionShowSlimeLine</el-checkbox><el-checkbox label="8">OptionShowSineLine</el-checkbox></el-checkbox-group></el-form-item><el-form-item label="DriverMath.Fonts"><el-checkbox-group v-model="form.DriverMath.Fonts" @change="generateCaptcha"><el-checkbox v-for="f in fonts" :label="f" :key="f">{{f}}</el-checkbox></el-checkbox-group></el-form-item><el-form-item label="DriverMath.BgColor."><el-form-item label="R"><el-slider v-model="form.DriverMath.BgColor.R" :min="0" :max="254" :step="1"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="G"><el-slider v-model="form.DriverMath.BgColor.G" :min="0" :max="254" :step="1"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="B"><el-slider v-model="form.DriverMath.BgColor.B" :min="0" :max="254" :step="1"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="A"><el-slider v-model="form.DriverMath.BgColor.A" :min="0" :max="254" :step="1"show-input@change="generateCaptcha"></el-slider></el-form-item></el-form-item></el-form></el-tab-pane><el-tab-pane label="DriverChinese" name="chinese"><el-formlabel-width="280px"label-position="left"><el-form-item label="DriverChinese.Length"><el-slider v-model="form.DriverChinese.Length" :min="1" :max="10" show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="DriverChinese.Source"><el-input v-model.trim="form.DriverChinese.Source"type="textarea"placeholder="可以是英文逗号分隔的词组"@change="generateCaptcha"></el-input></el-form-item><el-form-item label="DriverChinese.Width"><el-slider v-model="form.DriverChinese.Width" :min="20" :max="480" :step="5"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="DriverChinese.Height"><el-slider v-model="form.DriverChinese.Height" :min="20" :max="180" :step="5"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="DriverChinese.NoiseCount"><el-slider v-model="form.DriverChinese.NoiseCount" :min="0" :max="480" :step="5"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="DriverChinese.ShowLineOptions"><el-checkbox-group v-model="form.ShowLineOptions" @change="generateCaptcha"><el-checkbox label="2">OptionShowHollowLine</el-checkbox><el-checkbox label="4">OptionShowSlimeLine</el-checkbox><el-checkbox label="8">OptionShowSineLine</el-checkbox></el-checkbox-group></el-form-item><el-form-item label="DriverChinese.Fonts"><el-checkbox-group v-model="form.DriverChinese.Fonts" @change="generateCaptcha"><el-checkbox v-for="f in fonts" :label="f" :key="f">{{f}}</el-checkbox></el-checkbox-group></el-form-item><el-form-item label="DriverChinese.BgColor."><el-form-item label="R"><el-slider v-model="form.DriverChinese.BgColor.R" :min="0" :max="254" :step="1"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="G"><el-slider v-model="form.DriverChinese.BgColor.G" :min="0" :max="254" :step="1"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="B"><el-slider v-model="form.DriverChinese.BgColor.B" :min="0" :max="254" :step="1"show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="A"><el-slider v-model="form.DriverChinese.BgColor.A" :min="0" :max="254" :step="1"show-input@change="generateCaptcha"></el-slider></el-form-item></el-form-item></el-form></el-tab-pane><el-tab-pane label="DriverAudio" name="audio"><el-formlabel-width="280px"label-position="left"><el-form-item label="DriverAudio.Length"><el-slider v-model="form.DriverAudio.Length" :min="1" :max="10" show-input@change="generateCaptcha"></el-slider></el-form-item><el-form-item label="DriverAudio.Language"><el-radio-group v-model="form.DriverAudio.Language" @change="generateCaptcha"><el-radio-button label="en"></el-radio-button><el-radio-button label="zh"></el-radio-button><el-radio-button label="ru"></el-radio-button><el-radio-button label="ja"></el-radio-button></el-radio-group></el-form-item></el-form></el-tab-pane></el-tabs></el-main><el-footerstyle="line-height: 60px;margin-top: 1rem"> https://github.com/dejavuzhou/felix<a href="https://github.com/mojocn" type="success">https://github.com/mojocn</a><a href="https://mojotv.cn" type="success">Golang Tech Blog </a></el-footer></el-container></div>
</body><script>new Vue({el: '#app',data: function () {return {fonts: ["3Dumb.ttf","ApothecaryFont.ttf","Comismsh.ttf","DENNEthree-dee.ttf","DeborahFancyDress.ttf","Flim-Flam.ttf","RitaSmith.ttf","actionj.ttf","chromohv.ttf","wqy-microhei.ttc",],form: {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}},computed: {DriverStringSequencedCharacters: {get: function () {return this.form.DriverString.SequencedCharacters.join(',')},set: function (newValue) {this.form.DriverString.SequencedCharacters = newValue.split(',')}}},methods: {doShowLineOptions(val) {if (val > 0) {this.ShowLineOptions = this.form.ShowLineOptions | val;} else {}},formatTooltip: function (val) {var items = ['CaptchaModeNumber', 'CaptchaModeAlphabet', 'CaptchaModeArithmetic','CaptchaModeNumberAlphabet', 'CaptchaModeChinese', 'CaptchaModeUseSequencedCharacters'];return items[val];},handleClick: function (tab, event) {this.generateCaptcha();},generateCaptcha: function () {this.loading = true;//generate uuid string so the serve can verify numbers in the png//you can generate the captchaId in other wayvar that = this;var opt = 0;this.form.ShowLineOptions.forEach(item => {opt = opt | item;});this.form.DriverString.ShowLineOptions = opt;this.form.DriverMath.ShowLineOptions = opt;//this.form.DriverChineseWords.ShowLineOptions = opt;// the api/getCaptcha endpoint only recieve captchaId paramenteraxios.post('/api/getCaptcha', that.form).then(function (response) {that.loading = false;that.form.Id = response.data.captchaId;that.blob = response.data.data;}).catch(function (error) {that.loading = false;that.$notify({title: 500,message: 'net work or server error',type: "error"});});},submitForm: function () {var that = this;this.loading = true;axios.post('/api/verifyCaptcha', that.form).then(function (response) {that.loading = false;that.$notify({title: response.data.msg,message: response.data.data,type: response.data.code});if (response.data.code === "success") {that.generateCaptcha(false)}}).catch(function (error) {that.loading = false;that.$notify({title: 500,message: 'net work or server error',type: "error"});});}},mounted: function () {this.generateCaptcha()}})</script>
</html>
效果
适用场景
- 用户登录:防止暴力破解密码。
- 注册验证:确保注册用户为真人操作。
- 敏感操作:如修改密码、支付等操作前的二次验证。