geetest.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. package geetest
  2. import (
  3. "context"
  4. "crypto/md5"
  5. "encoding/hex"
  6. "fmt"
  7. "math"
  8. "math/rand"
  9. "strconv"
  10. "strings"
  11. "go-common/app/interface/main/account/conf"
  12. "go-common/app/interface/main/account/dao/geetest"
  13. "go-common/app/interface/main/account/model"
  14. "go-common/library/log"
  15. "github.com/pkg/errors"
  16. )
  17. // Service is
  18. type Service struct {
  19. c *conf.Config
  20. geetestDao *geetest.Dao
  21. }
  22. // New create service instance and return.
  23. func New(c *conf.Config) (s *Service) {
  24. s = &Service{
  25. c: c,
  26. geetestDao: geetest.New(c),
  27. }
  28. return
  29. }
  30. // Ping check server ok.
  31. func (s *Service) Ping(c context.Context) (err error) {
  32. return
  33. }
  34. // Close dao.
  35. func (s *Service) Close() {}
  36. // PreProcess preprocessing the geetest and get to challenge
  37. func (s *Service) PreProcess(c context.Context, req *model.GeeCaptchaRequest) (res *model.ProcessRes, err error) {
  38. var pre string
  39. res = &model.ProcessRes{}
  40. gc, clientType := s.geetestDao.GeeConfig(req.ClientType, s.c.Geetest)
  41. res.CaptchaID = gc.CaptchaID
  42. res.NewCaptcha = 1
  43. if pre, err = s.geetestDao.PreProcess(c, req.MID, clientType, gc, 1); err != nil || pre == "" {
  44. log.Error("s.geetestDao.PreProcess(%+v) err(%v)", req, err)
  45. randOne := md5.Sum([]byte(strconv.Itoa(rand.Intn(100))))
  46. randTwo := md5.Sum([]byte(strconv.Itoa(rand.Intn(100))))
  47. challenge := hex.EncodeToString(randOne[:]) + hex.EncodeToString(randTwo[:])[0:2]
  48. res.Challenge = challenge
  49. return
  50. }
  51. res.Success = 1
  52. slice := md5.Sum([]byte(pre + gc.PrivateKEY))
  53. res.Challenge = hex.EncodeToString(slice[:])
  54. log.Info("PreProcess success(%+v) ", res)
  55. return
  56. }
  57. // Validate recheck the challenge validate seccode
  58. func (s *Service) Validate(c context.Context, req *model.GeeCheckRequest) (stat bool) {
  59. if len(req.Validate) != 32 {
  60. log.Error("s.Validate(%+v) err(validate not eq 32byte)", req)
  61. stat = s.failbackValidate(c, req.Challenge, req.Validate, req.Seccode)
  62. return
  63. }
  64. gc, clientType := s.geetestDao.GeeConfig(req.ClientType, s.c.Geetest)
  65. slice := md5.Sum([]byte(gc.PrivateKEY + "geetest" + req.Challenge))
  66. if hex.EncodeToString(slice[:]) != req.Validate {
  67. log.Error("s.Validate(%+v) err(challenge not found)", req)
  68. return
  69. }
  70. res, err := s.geetestDao.Validate(c, req.Challenge, req.Seccode, clientType, gc.CaptchaID, req.MID)
  71. if err != nil {
  72. if errors.Cause(err) == context.DeadlineExceeded { // for geetest timeout
  73. stat = true
  74. return
  75. }
  76. log.Error("s.geetestDao.Validate(%+v) err(%v)", req, err)
  77. return
  78. }
  79. slice = md5.Sum([]byte(req.Seccode))
  80. stat = hex.EncodeToString(slice[:]) == res.Seccode
  81. return
  82. }
  83. //failbackValidate geetest failback model.
  84. func (s *Service) failbackValidate(c context.Context, challenge, validate, seccode string) bool {
  85. varr := strings.Split(validate, "_")
  86. if len(varr) < 3 {
  87. return false
  88. }
  89. encodeAns := varr[0]
  90. encodeFbii := varr[1]
  91. encodeIgi := varr[2]
  92. decodeAns := s.decodeResponse(challenge, encodeAns)
  93. decodeFbii := s.decodeResponse(challenge, encodeFbii)
  94. decodeIgi := s.decodeResponse(challenge, encodeIgi)
  95. return s.validateFailImage(decodeAns, decodeFbii, decodeIgi)
  96. }
  97. func (s *Service) decodeResponse(challenge, userresponse string) (res int) {
  98. if len(userresponse) > 100 {
  99. return
  100. }
  101. digits := []int{1, 2, 5, 10, 50}
  102. key := make(map[rune]int)
  103. for _, i := range challenge {
  104. if _, exist := key[i]; exist {
  105. continue
  106. }
  107. value := digits[len(key)%5]
  108. key[i] = value
  109. }
  110. for _, i := range userresponse {
  111. res += key[i]
  112. }
  113. res -= s.decodeRandBase(challenge)
  114. return
  115. }
  116. func (s *Service) decodeRandBase(challenge string) int {
  117. baseStr := challenge[32:]
  118. var tempList []int
  119. for _, char := range baseStr {
  120. tempChar := int(char)
  121. result := tempChar - 48
  122. if tempChar > 57 {
  123. result = tempChar - 87
  124. }
  125. tempList = append(tempList, result)
  126. }
  127. return tempList[0]*36 + tempList[1]
  128. }
  129. func (s *Service) md5Encode(values []byte) string {
  130. return fmt.Sprintf("%x", md5.Sum(values))
  131. }
  132. func (s *Service) validateFailImage(ans, fullBgIndex, imgGrpIndex int) bool {
  133. var thread float64 = 3
  134. fullBg := s.md5Encode([]byte(strconv.Itoa(fullBgIndex)))[0:10]
  135. imgGrp := s.md5Encode([]byte(strconv.Itoa(imgGrpIndex)))[10:20]
  136. var answerDecode []byte
  137. for i := 0; i < 9; i++ {
  138. if i%2 == 0 {
  139. answerDecode = append(answerDecode, fullBg[i])
  140. } else if i%2 == 1 {
  141. answerDecode = append(answerDecode, imgGrp[i])
  142. }
  143. }
  144. xDecode := answerDecode[4:]
  145. xInt64, err := strconv.ParseInt(string(xDecode), 16, 32)
  146. if err != nil {
  147. log.Error("%+v", err)
  148. }
  149. xInt := int(xInt64)
  150. result := xInt % 200
  151. if result < 40 {
  152. result = 40
  153. }
  154. return math.Abs(float64(ans-result)) < thread
  155. }