gin captcha
使用 gin + dchest/captcha 實現驗證碼功能
route.go
captchaGroup := r.Group("/captcha")
// 取得驗證碼圖片路徑
captchaGroup.GET("/", captchacontroller.NewCaptchaString)
// 取得驗證碼圖片檔案
captchaGroup.GET("/:captchaId", captchacontroller.GetCaptcha)
// demo 驗證
captchaGroup.POST("/verify", captchamiddleware.Verify(), captchacontroller.VerifyCaptcha)
middleware
func Verify() gin.HandlerFunc {
return func(c *gin.Context) {
var reqJSON captchacontroller.CaptchaRequest
if bindErr := c.ShouldBind(&reqJSON); bindErr != nil {
c.Abort()
return
}
// 驗證captcha
// The function deletes the captcha with the given id from the internal storage, so that the same captcha can't be verified anymore.
if !captchacontroller.VerifyString(reqJSON) {
c.Abort()
return
}
c.Next()
}
}
controller
type CaptchaResponse struct {
CaptchaId string `json:"captchaId"`
ImageUrl string `json:"imageUrl"`
}
type CaptchaRequest struct {
CaptchaId string `json:"captchaId" form:"captchaId" binding:"required,min=4"`
PngCode string `json:"pngCode" form:"pngCode" binding:"required,min=4"`
}
func NewCaptchaString(c *gin.Context) {
captcha := CaptchaString(captcha.DefaultLen)
c.JSON(http.StatusOK, captcha)
}
// reload: 有直的話, 可以重新產生圖片(captchaId復用)
func GetCaptcha(c *gin.Context) {
captchaId := c.Param("captchaId")
fmt.Println("GetCaptchaPng : " + captchaId)
ServeHTTP(c.Writer, c.Request)
}
// 產生 lenth長度驗證碼
func CaptchaString(length int) CaptchaResponse {
if length <= 0 {
length = captcha.DefaultLen
}
captchaId := captcha.NewLen(length)
var captcha CaptchaResponse
captcha.CaptchaId = captchaId
captcha.ImageUrl = "/captcha/" + captchaId + ".png"
return captcha
}
// 驗證captcha, id 跟 輸入
func VerifyString(cap CaptchaRequest) bool {
// The function deletes the captcha with the given id from the internal storage, so that the same captcha can't be verified anymore.
return captcha.VerifyString(cap.CaptchaId, cap.PngCode)
}
func ServeHTTP(w http.ResponseWriter, r *http.Request) {
dir, file := path.Split(r.URL.Path)
fmt.Println("dir : " + dir)
ext := path.Ext(file)
id := file[:len(file)-len(ext)]
fmt.Println("file : " + file)
fmt.Println("ext : " + ext)
fmt.Println("id : " + id)
if ext == "" || id == "" {
http.NotFound(w, r)
return
}
fmt.Println("reload : " + r.FormValue("reload"))
if r.FormValue("reload") != "" {
captcha.Reload(id)
}
lang := strings.ToLower(r.FormValue("lang"))
download := path.Base(dir) == "download"
if Serve(w, r, id, ext, lang, download, captcha.StdWidth, captcha.StdHeight) == captcha.ErrNotFound {
http.NotFound(w, r)
}
}
func Serve(w http.ResponseWriter, r *http.Request, id, ext, lang string, download bool, width, height int) error {
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
w.Header().Set("Pragma", "no-cache")
w.Header().Set("Expires", "0")
var content bytes.Buffer
switch ext {
case ".png":
w.Header().Set("Content-Type", "image/png")
captcha.WriteImage(&content, id, width, height)
case ".wav":
w.Header().Set("Content-Type", "audio/x-wav")
captcha.WriteAudio(&content, id, lang)
default:
return captcha.ErrNotFound
}
if download {
w.Header().Set("Content-Type", "application/octet-stream")
}
http.ServeContent(w, r, id+ext, time.Time{}, bytes.NewReader(content.Bytes()))
return nil
}
// 測試middleware用
func VerifyCaptcha(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"data": "okoko",
})
}