geetestlib.go 3.7 KB
package geetest

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"
)

type GeetestLib struct {
	CaptchaID  string
	PrivateKey string
	Client     *http.Client
}

type FailbackRegisterRespnse struct {
	Success    int    `json:"success"`
	GT         string `json:"gt"`
	Challenge  string `json:"challenge"`
	NewCaptcha int    `json:"new_captcha"`
}

const (
	geetestHost = "http://api.geetest.com"
	registerURL = geetestHost + "/register.php"
	validateURL = geetestHost + "/validate.php"
)

func MD5Encode(input string) string {
	md5Instant := md5.New()
	md5Instant.Write([]byte(input))
	return hex.EncodeToString(md5Instant.Sum(nil))
}

// 初始化 GeetestLib
func NewGeetestLib(capthcaID string, privateKey string, timeOut time.Duration) (geetest GeetestLib){
	client := &http.Client{Timeout: timeOut}
	geetest = GeetestLib{capthcaID, privateKey, client}
	return
}

func (g *GeetestLib) getFailBackRegisterResponse(success int, challenge string) []byte {
	if challenge == "" {
		challenge = hex.EncodeToString(md5.New().Sum(nil))
	}

	response := FailbackRegisterRespnse{
		success,
		g.CaptchaID,
		challenge,
		1,
	}
	res, _ := json.Marshal(response)
	return res
}

func (g *GeetestLib) do(req *http.Request) (body []byte, err error) {
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	var resp *http.Response
	if resp, err = g.Client.Do(req); err != nil {
		return
	}
	defer resp.Body.Close()
	if resp.StatusCode >= http.StatusInternalServerError {
		err = errors.New("http status code 5xx")
		return
	}

	if body, err = ioutil.ReadAll(resp.Body); err != nil {
		return
	}
	return
}

func (g *GeetestLib) PreProcess(userID string, userIP string) (int8, []byte) {
	params := url.Values{}
	params.Add("gt", g.CaptchaID)
	params.Add("new_captcha", "1")
	if userID != "" {
		params.Add("user_id", userID)
	}
	if userIP != "" {
		params.Add("ip_adress", userIP)
	}
	req, _ := http.NewRequest("GET", registerURL+"?"+params.Encode(), nil)
	body, err := g.do(req)
	if err != nil {
		return 0, g.getFailBackRegisterResponse(0, "")
	}
	challenge := string(body)
	if len(challenge) != 32 {
		return 0, g.getFailBackRegisterResponse(0, "")
	} else {
		challenge = MD5Encode(challenge + g.PrivateKey)
		return 1, g.getFailBackRegisterResponse(1, challenge)
	}
}

func (g *GeetestLib) checkParas(challenge string, validate string, seccode string) bool {
	if challenge == "" || validate == "" || seccode == "" {
		return false
	}
	return true
}

func (g *GeetestLib) checkSuccessRes(challenge string, validate string) bool {
	return MD5Encode(g.PrivateKey+"geetest"+challenge) == validate
}

func (g *GeetestLib) checkFailbackRes(challenge string, validate string) bool {
	return MD5Encode(challenge) == validate
}

func (g *GeetestLib) SuccessValidate(challenge string, validate string, seccode string, userID string, userIP string) bool {
	if !g.checkParas(challenge, validate, seccode) {
		return false
	}
	if !g.checkSuccessRes(challenge, validate) {
		return false
	}
	params := url.Values{}
	params.Add("seccode", seccode)
	params.Add("challenge", challenge)
	params.Add("captchaid", g.CaptchaID)
	params.Add("sdk", "golang_v1.0.0")
	if userID != "" {
		params.Add("user_id", userID)
	}
	if userIP != "" {
		params.Add("ip_adress", userIP)
	}
	req, _ := http.NewRequest("POST", validateURL, strings.NewReader(params.Encode()))
	body, err := g.do(req)
	if err != nil {
		return false
	}
	res := string(body)
	return res == MD5Encode(seccode)
}

func (g *GeetestLib) FailbackValidate(challenge string, validate string, seccode string) bool {
	if !g.checkParas(challenge, validate, seccode) {
		return false
	}
	if !g.checkFailbackRes(challenge, validate) {
		return false
	}
	return true
}