go语言之生成验证码

golang中实现验证码的库有很多,本例子中以base64Captcha来实现生成图片验证码,效果如图:

图片验证码依赖库地址为:

go get github.com/mojocn/base64Captcha

实现验证码的主要步骤如下:

创建图片验证码存储对象

创建图片验证码对象有两种方式,一种是默认对象,一种是自定义对象

1、创建默认对象

使用DefaultMemStore 创建的对象,存储的验证码为 10240 个,过期时间为 10分钟,方法如下:

var captchaObj = base64Captcha.DefaultMemStore

2、创建自定义对象

自定义对象可以更改验证码存储上限,并可以自定义过期时间,如下:

var captchaObj = base64Captcha.NewMemoryStore(20000, 3*time.Minute)

配置各种类型的图片验证码

1、配置图形算术验证码,如下:

func mathConfig() *base64Captcha.DriverMath {
	mathType := &base64Captcha.DriverMath{
		Height:          50,
		Width:           100,
		NoiseCount:      0,
		ShowLineOptions: base64Captcha.OptionShowHollowLine,
		BgColor: &color.RGBA{
			R: 40,
			G: 30,
			B: 89,
			A: 29,
		},
		Fonts: nil,
	}
	return mathType
}

2、配置图形数字验证码,如下:

func digitConfig() *base64Captcha.DriverDigit {
	digitType := &base64Captcha.DriverDigit{
		Height:   50,
		Width:    100,
		Length:   5,
		MaxSkew:  0.45,
		DotCount: 10,          //干扰线
	}
	return digitType
}

3、配置图形字符串验证码,如下:

func stringConfig() *base64Captcha.DriverString {
	stringType := &base64Captcha.DriverString{
		Height:          100,
		Width:           100,
		NoiseCount:      0,
		ShowLineOptions: base64Captcha.OptionShowHollowLine | base64Captcha.OptionShowSlimeLine,
		Length:          4,
		Source:          "helloworldchinashenzhen93567210guagndong9",
		BgColor: &color.RGBA{
			R: 40,
			G: 30,
			B: 89,
			A: 29,
		},
		Fonts: nil,
	}
	return stringType
}

4、配置图形汉字验证码,如下:

func chineseConfig() *base64Captcha.DriverChinese {
        chineseType := &base64Captcha.DriverChinese{
                Height:          50,
                Width:           500,
                NoiseCount:      0,
                ShowLineOptions: base64Captcha.OptionShowSlimeLine,
                Length:          2,
                Source:          "中国,广东,深圳市,宝安区,南山区,福田区,万里路,福永",
                BgColor: &color.RGBA{
                        R: 40,
                        G: 30,
                        B: 89,
                        A: 29,
                },
                Fonts: nil,
        }
        return chineseType
}

5、配置图形化数字音频验证码,如下:

func autoConfig() *base64Captcha.DriverAudio {
	chineseType := &base64Captcha.DriverAudio{
		Length:   4,
		Language: "zh",
	}
	return chineseType
}

创建图片验证码

func CreateCode() (string, string, error) {
	var driver base64Captcha.Driver
	switch viper.GetString("code.captcha_type") {
	case "audio":
		driver = autoConfig()
	case "string":
		driver = stringConfig()
	case "math":
		driver = mathConfig()
	case "chinese":
		driver = chineseConfig()
	case "digit":
		driver = digitConfig()
	}
	if driver == nil {
		panic("请在yaml文件中配置验证码类型")
	}
	// 创建验证码并传入创建的类型的配置,以及存储的对象
	c := base64Captcha.NewCaptcha(driver, captchaObj)
	id, b64s, err := c.Generate()
	return id, b64s, err
}

校验验证码

func VerifyCaptcha(id, VerifyValue string) bool {

	return captchaObj.Verify(id, VerifyValue, true)
}
  • Verify()第三个参数为 true 时,校验传入的id 验证码,校验完后ID对应验证码会在内存中删除
  • Verify()第三个参数为 true 时,校验传入的id 验证码,校验完后ID对应验证码不会在内存中删除

获取验证码

这里获取的是服务端生成的验证码的答案,比如你生成的算术验证码为1+2,那么这里获取的就是3

func GetCodeAnswer(codeId string) string {
	//captchaObj为最开始创建的图片验证码存储对象
	return captchaObj.Get(codeId, false)
}
  • Get()函数中第二个参数为true时,根据ID获取完验证码就要删除这个验证码
  • Get()函数中第二个参数为false时,根据ID获取完验证码不会删除这个验证码

完整代码示例

1、首先定义包captcha,在其中定义配置文件config.yaml,用于指定读取哪个配置,如图:

注:此配置文件中选择的是数字验证码

2、在同级目录下定义文件config.go,用于映射配置文件的结构体,如图:

3、定义文件captchastyle.go,其中定义验证码类型以及获取验证码,如下:

package captcha

import (
	"image/color"
	"io/ioutil"
	"log"

	"github.com/mojocn/base64Captcha"
	"gopkg.in/yaml.v2"
)

//图形化算术验证码
func mathConfig() *base64Captcha.DriverMath {
	mathType := &base64Captcha.DriverMath{
		Height:          50,
		Width:           100,
		NoiseCount:      0,
		ShowLineOptions: base64Captcha.OptionShowHollowLine,
		BgColor: &color.RGBA{
			R: 40,
			G: 30,
			B: 89,
			A: 29,
		},
		Fonts: nil,
	}
	return mathType
}

//图像化数字验证码
func digitConfig() *base64Captcha.DriverDigit {
	digitType := &base64Captcha.DriverDigit{
		Height:   50,
		Width:    100,
		Length:   5,
		MaxSkew:  0.45,
		DotCount: 10,
	}
	return digitType
}

//图形化字符串验证码
func stringConfig() *base64Captcha.DriverString {
	stringType := &base64Captcha.DriverString{
		Height:          100,
		Width:           100,
		NoiseCount:      0,
		ShowLineOptions: base64Captcha.OptionShowHollowLine | base64Captcha.OptionShowSlimeLine,
		Length:          4,
		Source:          "helloworldchinashenzhen93567210guagndong9",
		BgColor: &color.RGBA{
			R: 40,
			G: 30,
			B: 89,
			A: 29,
		},
		Fonts: nil,
	}
	return stringType
}

//图形化汉字验证码
func chineseConfig() *base64Captcha.DriverChinese {
	chineseType := &base64Captcha.DriverChinese{
		Height:          50,
		Width:           500,
		NoiseCount:      0,
		ShowLineOptions: base64Captcha.OptionShowSlimeLine,
		Length:          2,
		Source:          "中国,广东,深圳市,宝安区,南山区,福田区,万里路,福永",
		BgColor: &color.RGBA{
			R: 40,
			G: 30,
			B: 89,
			A: 29,
		},
		Fonts: nil,
	}
	return chineseType
}

//图形化音频验证码
func autoConfig() *base64Captcha.DriverAudio {
	chineseType := &base64Captcha.DriverAudio{
		Length:   4,
		Language: "zh",
	}
	return chineseType
}

//创建验证码
func CreateCpatcha(bs base64Captcha.Store) (string, string, error) {
	var driver base64Captcha.Driver
	yamlfile, err := ioutil.ReadFile("captcha/config.yaml")
	if err != nil {
		log.Fatal(err)
	}
	var cn *Config
	err = yaml.Unmarshal(yamlfile, &cn)
	if err != nil {
		log.Fatal(err)
	}
	switch cn.Driver {
	case "math":
		driver = mathConfig()
	case "string":
		driver = stringConfig()
	case "chinese":
		driver = chineseConfig()
	case "digit":
		driver = digitConfig()
	case "audio":
		driver = autoConfig()
	}
	//	driver = mathConfig()
	if driver == nil {
		log.Fatal("请检查配置文件中是否没有指定验证码类型!!!")
	}
	c := base64Captcha.NewCaptcha(driver, bs)
	id, b64s, err := c.Generate()
	return id, b64s, err
}

//校验验证码,接收用户输入过来的
/*
  * 验证码id是哪里来的, 是客户端调用服务端接口,服务端生成验证码的同时会生成
    一个验证码id, 然后客户端输入验证码登录的时候会将输入的验证码和这个id再次
    发送回服务端
  * VerifyCaptcha会接收客户端传递回来的验证码id和用户输入的验证码的值,然后将
    id传递给函数GetCodeAnswer()来根据id获取服务端验证码答案,然后将用户输入的
    与服务器端的答案做比较如果相同返回true,否则返回false
*/
func VerifyCaptcha(id, VerifyValue string, bs base64Captcha.Store) bool {
	str := getCodeAnswer(bs, id)
	if str == VerifyValue {
		return true
	}
	return false
	//	return result.Verify(id, VerifyValue, true)
}

//获取验证码答案
func getCodeAnswer(bs base64Captcha.Store, codeId string) string {
	//bs为main()函数中创建的验证码存储对象captchaObj传递过来的
	return bs.Get(codeId, false)
}

4、最后创建主配置文件,如下:

主配置文件中中定义了两个接口,GET用来获取验证码和验证码id,调用POST的时候,将用户输入的验证码和GET获取的id一起传递过去,来校验下用户输入的验证码和服务生成的验证码是否一致,一致就返回true

package main

import (
	"fmt"
	"log"
	"project/captcha"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/mojocn/base64Captcha"
)

type verifycaptcha struct {
	CaptchaID    string `json:"captchaid"`
	CaptchaValue string `json:"captchavalue"`
}

func main() {
	var captchaObj = base64Captcha.NewMemoryStore(20000, 3*time.Minute)
	router := gin.New()
	//调用接口获取验证码
	router.GET("/api/v1/auth/captcha", func(c *gin.Context) {
		//创建图片验证码
		id, b64s, err := captcha.CreateCpatcha(captchaObj)
		if err != nil {
			log.Fatal(err)
		}
		data := make(map[string]interface{})
		data["verifyID"] = id
		data["captcha"] = b64s

		c.JSON(200, gin.H{
			"code": "00000",
			"msg":  "OK",
			"data": data,
		})
	})
	//调用接口校验验证码
	router.POST("/api/v1/auth/verifycaptcha", func(c *gin.Context) {
		var v verifycaptcha
		err := c.ShouldBindJSON(&v)
		if err != nil {
			fmt.Println(err)
			return
		}
		res := captcha.VerifyCaptcha(v.CaptchaID, v.CaptchaValue, captchaObj)
		c.JSON(400, gin.H{
			"code": "10000",
			"msg":  res,
		})
	})
	router.Run()
}

5、通过postman测试调用GET接口来获取验证码,如图:

  • captcha:服务端生成的图片验证码,这里是以base64方式显示,图片中显示为65532
  • verifyID:图片验证码

6、通过postman调用POST接口,将上述图片ID和用户输入的验证码传递过去,与服务端生成的验证码做比较,如图:

返回结果为true,说明用户输入的与服务端生成的一致

注意:base64格式的图片如何要查看需要先进行转换或者直接将其放在html的<img src=””>标签中即可

标签