geetest.go 4.3 KB

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