You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
169 lines
4.5 KiB
169 lines
4.5 KiB
package geetest |
|
|
|
import ( |
|
"context" |
|
"crypto/md5" |
|
"encoding/hex" |
|
"fmt" |
|
"math" |
|
"math/rand" |
|
"strconv" |
|
"strings" |
|
|
|
"go-common/app/interface/main/account/conf" |
|
"go-common/app/interface/main/account/dao/geetest" |
|
"go-common/app/interface/main/account/model" |
|
"go-common/library/log" |
|
|
|
"github.com/pkg/errors" |
|
) |
|
|
|
// Service is |
|
type Service struct { |
|
c *conf.Config |
|
geetestDao *geetest.Dao |
|
} |
|
|
|
// New create service instance and return. |
|
func New(c *conf.Config) (s *Service) { |
|
s = &Service{ |
|
c: c, |
|
geetestDao: geetest.New(c), |
|
} |
|
return |
|
} |
|
|
|
// Ping check server ok. |
|
func (s *Service) Ping(c context.Context) (err error) { |
|
return |
|
} |
|
|
|
// Close dao. |
|
func (s *Service) Close() {} |
|
|
|
// PreProcess preprocessing the geetest and get to challenge |
|
func (s *Service) PreProcess(c context.Context, req *model.GeeCaptchaRequest) (res *model.ProcessRes, err error) { |
|
var pre string |
|
res = &model.ProcessRes{} |
|
gc, clientType := s.geetestDao.GeeConfig(req.ClientType, s.c.Geetest) |
|
res.CaptchaID = gc.CaptchaID |
|
res.NewCaptcha = 1 |
|
if pre, err = s.geetestDao.PreProcess(c, req.MID, clientType, gc, 1); err != nil || pre == "" { |
|
log.Error("s.geetestDao.PreProcess(%+v) err(%v)", req, err) |
|
randOne := md5.Sum([]byte(strconv.Itoa(rand.Intn(100)))) |
|
randTwo := md5.Sum([]byte(strconv.Itoa(rand.Intn(100)))) |
|
challenge := hex.EncodeToString(randOne[:]) + hex.EncodeToString(randTwo[:])[0:2] |
|
res.Challenge = challenge |
|
return |
|
} |
|
res.Success = 1 |
|
slice := md5.Sum([]byte(pre + gc.PrivateKEY)) |
|
res.Challenge = hex.EncodeToString(slice[:]) |
|
log.Info("PreProcess success(%+v) ", res) |
|
return |
|
} |
|
|
|
// Validate recheck the challenge validate seccode |
|
func (s *Service) Validate(c context.Context, req *model.GeeCheckRequest) (stat bool) { |
|
if len(req.Validate) != 32 { |
|
log.Error("s.Validate(%+v) err(validate not eq 32byte)", req) |
|
stat = s.failbackValidate(c, req.Challenge, req.Validate, req.Seccode) |
|
return |
|
} |
|
gc, clientType := s.geetestDao.GeeConfig(req.ClientType, s.c.Geetest) |
|
slice := md5.Sum([]byte(gc.PrivateKEY + "geetest" + req.Challenge)) |
|
if hex.EncodeToString(slice[:]) != req.Validate { |
|
log.Error("s.Validate(%+v) err(challenge not found)", req) |
|
return |
|
} |
|
res, err := s.geetestDao.Validate(c, req.Challenge, req.Seccode, clientType, gc.CaptchaID, req.MID) |
|
if err != nil { |
|
if errors.Cause(err) == context.DeadlineExceeded { // for geetest timeout |
|
stat = true |
|
return |
|
} |
|
log.Error("s.geetestDao.Validate(%+v) err(%v)", req, err) |
|
return |
|
} |
|
slice = md5.Sum([]byte(req.Seccode)) |
|
stat = hex.EncodeToString(slice[:]) == res.Seccode |
|
return |
|
} |
|
|
|
//failbackValidate geetest failback model. |
|
func (s *Service) failbackValidate(c context.Context, challenge, validate, seccode string) bool { |
|
varr := strings.Split(validate, "_") |
|
if len(varr) < 3 { |
|
return false |
|
} |
|
encodeAns := varr[0] |
|
encodeFbii := varr[1] |
|
encodeIgi := varr[2] |
|
decodeAns := s.decodeResponse(challenge, encodeAns) |
|
decodeFbii := s.decodeResponse(challenge, encodeFbii) |
|
decodeIgi := s.decodeResponse(challenge, encodeIgi) |
|
return s.validateFailImage(decodeAns, decodeFbii, decodeIgi) |
|
} |
|
|
|
func (s *Service) decodeResponse(challenge, userresponse string) (res int) { |
|
if len(userresponse) > 100 { |
|
return |
|
} |
|
digits := []int{1, 2, 5, 10, 50} |
|
key := make(map[rune]int) |
|
for _, i := range challenge { |
|
if _, exist := key[i]; exist { |
|
continue |
|
} |
|
value := digits[len(key)%5] |
|
key[i] = value |
|
} |
|
for _, i := range userresponse { |
|
res += key[i] |
|
} |
|
res -= s.decodeRandBase(challenge) |
|
return |
|
} |
|
|
|
func (s *Service) decodeRandBase(challenge string) int { |
|
baseStr := challenge[32:] |
|
var tempList []int |
|
for _, char := range baseStr { |
|
tempChar := int(char) |
|
result := tempChar - 48 |
|
if tempChar > 57 { |
|
result = tempChar - 87 |
|
} |
|
tempList = append(tempList, result) |
|
} |
|
return tempList[0]*36 + tempList[1] |
|
} |
|
|
|
func (s *Service) md5Encode(values []byte) string { |
|
return fmt.Sprintf("%x", md5.Sum(values)) |
|
} |
|
|
|
func (s *Service) validateFailImage(ans, fullBgIndex, imgGrpIndex int) bool { |
|
var thread float64 = 3 |
|
fullBg := s.md5Encode([]byte(strconv.Itoa(fullBgIndex)))[0:10] |
|
imgGrp := s.md5Encode([]byte(strconv.Itoa(imgGrpIndex)))[10:20] |
|
var answerDecode []byte |
|
for i := 0; i < 9; i++ { |
|
if i%2 == 0 { |
|
answerDecode = append(answerDecode, fullBg[i]) |
|
} else if i%2 == 1 { |
|
answerDecode = append(answerDecode, imgGrp[i]) |
|
} |
|
} |
|
xDecode := answerDecode[4:] |
|
xInt64, err := strconv.ParseInt(string(xDecode), 16, 32) |
|
if err != nil { |
|
log.Error("%+v", err) |
|
} |
|
xInt := int(xInt64) |
|
result := xInt % 200 |
|
if result < 40 { |
|
result = 40 |
|
} |
|
return math.Abs(float64(ans-result)) < thread |
|
}
|
|
|