base64Captcha Documentation

repository·master·Indexed 25 days ago

https://github.com/mojocn/base64captcha

A flexible Go package for generating various types of captchas—including digit, string, math, Chinese, and audio—encoded as Base64 strings. It features a customizable architecture using Driver and Store interfaces, allowing for custom rendering logic and flexible persistence backends such as the built-in DefaultMemStore or distributed systems like Etcd and Redis.

Tokens
9.7K
Snippets
12
Records
68
Agent score
82%

What's inside base64Captcha

  1. Implement Captcha with Etcd as a storage backend

    master

    To use etcd as a distributed store for captcha answers, you can wrap the base64Captcha.DriverString in a custom struct. This allows you to persist captcha answers in etcd with a TTL (Time To Live), ensuring that answers expire automatically after a set duration.

    Implementation Steps:

    1. Define a custom struct: Embed *base64Captcha.DriverString and include your etcd client.
    2. Constructor: Use base64Captcha.NewDriverString to initialize the base driver, then wrap it in your custom struct.
    3. Generate with Persistence: In your generation method, call GenerateIdQuestionAnswer() to get the ID and answer, then use etcd.Grant to create a lease and etcd.Put to store the answer associated with the captcha ID.
    4. Verify against Etcd: In your verification method, retrieve the value from etcd using the captcha ID and compare it with the user's provided answer.
    // CaptchaEtcd base64 captcha with etcd
    type CaptchaEtcd struct {
    	*base64Captcha.DriverString
    	store *etcd.Client
    }
    
    // NewClientEtcd constructor
    func NewClientEtcd(height, width int, store *etcd.Client) *CaptchaEtcd {
    	d := base64Captcha.NewDriverString(height, width, 0, 0, 4, "%#=qwe23456789rtyupasdfghjkzxcvbnm", &color.RGBA{0, 0, 0, 0}, []string{"wqy-microhei.ttc"})
    	cli := &CaptchaEtcd{store: store}
    	cli.DriverString = d
    	return cli
    }
    
    // GenerateIdAndImage creates image and stores answer in etcd
    func (c *CaptchaEtcd) GenerateIdAndImage() (id, b64s, ans string, err error) {
    	id, content, answer := c.GenerateIdQuestionAnswer()
    	item, err := c.DrawCaptcha(content)
    	if err != nil {
    		return "", "", "", err
    	}
    	// expire in 120s
    	grantResp, err := c.store.Grant(context.TODO(), 120)
    	if err != nil {
    		return "", "", "", err
    	}
    	ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
    	_, err = c.store.Put(ctx, captchaPrefix+id, answer, clientv3.WithLease(grantResp.ID))
    	cancel()
    	if err != nil {
    		return "", "", "", err
    	}
    	b64s = item.EncodeB64string()
    	return id, b64s, answer, nil
    }
    
    // Verify checks captcha answer against etcd
    func (c *CaptchaEtcd) Verify(id, answer string) (match bool, err error) {
    	ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
    	key := captchaPrefix + id
    	resp, err := c.store.Get(ctx, key)
    	cancel()
    	if err != nil {
    		return false, err
    	}
    
    	for _, ev := range resp.Kvs {
    		if string(ev.Value) == answer {
    			return true, nil
    		}
    	}
    	return false, nil
    }
  2. Embed font files using go-bindata

    master

    To include font files within the base64Captcha package, you can use the go-bindata tool. This process involves generating a Go file from the fonts directory and then modifying the package name to ensure it integrates correctly with the library.

    1. Install/Use go-bindata to process the fonts directory.
    2. Update the generated bindata.go file to use package base64Captcha instead of package main.
    go-bindata fonts
    sed -i "s/package main/package base64Captcha/g" bindata.go
  3. Use the Captcha engine to generate and verify captchas

    master

    The Captcha struct is the main entry point. It requires a Driver and a Store passed to NewCaptcha.

    Generating a Captcha

    Use c.Generate() to produce a unique ID and a Base64 encoded string of the captcha (image or audio).

    Verifying an Answer

    Use c.Verify(id, answer, clear) to check if the user's input matches the stored answer. If clear is true, the answer is removed from the store after verification.

  4. Install base64Captcha

    master

    To install the latest version of base64Captcha, use the following command:

    go get -u github.com/mojocn/base64Captcha

    If you are in mainland China and encounter failures downloading golang.org/x/image, ensure your Go version is > 1.11 and set the following environment variable:

    export GOPROXY=https://goproxy.io

    To use a specific historical version (e.g., v1.2.2), use:

    go get github.com/mojocn/base64Captcha@v1.2.2
    go get -u github.com/mojocn/base64Captcha
  5. Supported language codes for DriverLanguage

    master

    When setting the languageCode in NewDriverLanguage, you can use the following keys to define the Unicode range for the generated characters:

    • latin: Standard Latin characters.
    • zh: Chinese characters.
    • ko: Korean characters.
    • jp: Japanese characters.
    • ru: Russian characters.
    • th: Thai characters.
    • greek: Greek characters.
    • arabic: Arabic characters.
    • hebrew: Hebrew characters.

    If an invalid code is provided, the driver defaults to latin.

  6. Full HTTP Server Example

    master

    This example demonstrates how to integrate base64Captcha into a net/http server. It includes endpoints to generate different types of captchas (audio, string, math, chinese, digit) and an endpoint to verify the user's answer.

    Note that for drivers like DriverString, DriverMath, and DriverChinese, you should call .ConvertFonts() to prepare the driver for use.

    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)
    	}
    }
  7. Implement the Store interface

    master

    The Store interface is responsible for persisting captcha answers associated with a unique ID. You can use the built-in DefaultMemStore for in-memory storage or implement your own (e.g., using Etcd or Redis) to support distributed systems.

    Required methods:

    • Set(id string, value string): Sets the answer for a specific captcha ID.
    • Get(id string, clear bool) string: Retrieves the answer. If clear is true, the entry should be deleted from the store.
    • Verify(id, answer string, clear bool) bool: Directly verifies the answer and optionally clears it.
    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
    }
  8. Implement the Driver interface

    master

    The Driver interface defines how captcha content is generated and how it is visually/audibly rendered. You can use built-in drivers like DriverDigit, DriverString, DriverMath, or DriverChinese.

    Required methods:

    • GenerateIdQuestionAnswer() (id, q, a string): Creates a random ID, the question content, and the correct answer.
    • DrawCaptcha(content string) (item Item, err error): Draws the captcha content into an Item (which can be encoded to Base64).
    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)
    }
  9. Configure the Digit driver with DriverDigit

    master

    The DriverDigit struct is used to configure the parameters for a digit-based captcha. You can customize the image dimensions, the number of digits required, the skew factor for distortion, and the density of background noise (dots).

    Key configuration fields:

    • Height: PNG height in pixels.
    • Width: PNG width in pixels.
    • Length: The number of digits in the captcha solution.
    • MaxSkew: The maximum absolute skew factor applied to a single digit.
    • DotCount: The number of background circles to be drawn.
  10. Initialize an ItemChar captcha item

    master
    Use NewItemChar to create a new captcha item instance. This initializes a canvas with a specified width, height, and background color. The resulting *ItemChar can be used to draw text, noise, and lines to generate a captcha image.
  11. Draw a captcha image with DrawCaptcha

    master

    The DrawCaptcha method renders the captcha image based on the driver's configuration and the provided text content. It handles background color selection, line drawing (hollow, slime, or sine), noise generation, and text rendering.

    Returns:

    • item (Item): The rendered captcha item containing the image data.
    • err (error): An error if drawing fails (e.g., font loading issues).
  12. Initialize an in-memory store with NewMemoryStore

    master

    Use NewMemoryStore to create a standard in-memory storage implementation for captcha IDs and their values. This store is useful for local development or single-instance applications.

    To use this store, you must register it with SetCustomStore to replace the default storage mechanism.

    Parameters:

    • collectNum (int): The threshold of items stored that triggers an asynchronous garbage collection of expired captchas.
    • expiration (time.Duration): The duration after which a captcha is considered expired.