GeetestLib.php 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. <?php
  2. namespace App\Utils;
  3. /**
  4. * 极验行为式验证安全平台,php 网站主后台包含的库文件
  5. *
  6. * @author Tanxu
  7. */
  8. class GeetestLib
  9. {
  10. const GT_SDK_VERSION = 'php_3.2.0';
  11. public static $connectTimeout = 3;
  12. public static $socketTimeout = 3;
  13. private $response;
  14. public function __construct($captcha_id, $private_key)
  15. {
  16. $this->captcha_id = $captcha_id;
  17. $this->private_key = $private_key;
  18. }
  19. /**
  20. * 判断极验服务器是否down机
  21. *
  22. * @param null $user_id
  23. * @return int
  24. */
  25. public function pre_process($user_id = null)
  26. {
  27. $url = "http://api.geetest.com/register.php?gt=" . $this->captcha_id;
  28. if (($user_id != null) and (is_string($user_id))) {
  29. $url = $url . "&user_id=" . $user_id;
  30. }
  31. $challenge = $this->send_request($url);
  32. if (strlen($challenge) != 32) {
  33. $this->failback_process();
  34. return 0;
  35. }
  36. $this->success_process($challenge);
  37. return 1;
  38. }
  39. /**
  40. * @param $challenge
  41. */
  42. private function success_process($challenge)
  43. {
  44. $challenge = md5($challenge . $this->private_key);
  45. $result = array(
  46. 'success' => 1,
  47. 'gt' => $this->captcha_id,
  48. 'challenge' => $challenge
  49. );
  50. $this->response = $result;
  51. }
  52. /**
  53. *
  54. */
  55. private function failback_process()
  56. {
  57. $rnd1 = md5(rand(0, 100));
  58. $rnd2 = md5(rand(0, 100));
  59. $challenge = $rnd1 . substr($rnd2, 0, 2);
  60. $result = array(
  61. 'success' => 0,
  62. 'gt' => $this->captcha_id,
  63. 'challenge' => $challenge
  64. );
  65. $this->response = $result;
  66. }
  67. /**
  68. * @return mixed
  69. */
  70. public function get_response_str()
  71. {
  72. return json_encode($this->response);
  73. }
  74. /**
  75. * 返回数组方便扩展
  76. *
  77. * @return mixed
  78. */
  79. public function get_response()
  80. {
  81. return $this->response;
  82. }
  83. /**
  84. * 正常模式获取验证结果
  85. *
  86. * @param $challenge
  87. * @param $validate
  88. * @param $seccode
  89. * @param null $user_id
  90. * @return int
  91. */
  92. public function success_validate($challenge, $validate, $seccode, $user_id = null)
  93. {
  94. if (!$this->check_validate($challenge, $validate)) {
  95. return 0;
  96. }
  97. $data = array(
  98. "seccode" => $seccode,
  99. "sdk" => self::GT_SDK_VERSION,
  100. );
  101. if (($user_id != null) and (is_string($user_id))) {
  102. $data["user_id"] = $user_id;
  103. }
  104. $url = "http://api.geetest.com/validate.php";
  105. $codevalidate = $this->post_request($url, $data);
  106. if ($codevalidate == md5($seccode)) {
  107. return 1;
  108. } else {
  109. if ($codevalidate == "false") {
  110. return 0;
  111. } else {
  112. return 0;
  113. }
  114. }
  115. }
  116. /**
  117. * 宕机模式获取验证结果
  118. *
  119. * @param $challenge
  120. * @param $validate
  121. * @param $seccode
  122. * @return int
  123. */
  124. public function fail_validate($challenge, $validate, $seccode)
  125. {
  126. if ($validate) {
  127. $value = explode("_", $validate);
  128. $ans = $this->decode_response($challenge, $value['0']);
  129. $bg_idx = $this->decode_response($challenge, $value['1']);
  130. $grp_idx = $this->decode_response($challenge, $value['2']);
  131. $x_pos = $this->get_failback_pic_ans($bg_idx, $grp_idx);
  132. $answer = abs($ans - $x_pos);
  133. if ($answer < 4) {
  134. return 1;
  135. } else {
  136. return 0;
  137. }
  138. } else {
  139. return 0;
  140. }
  141. }
  142. /**
  143. * @param $challenge
  144. * @param $validate
  145. * @return bool
  146. */
  147. private function check_validate($challenge, $validate)
  148. {
  149. if (strlen($validate) != 32) {
  150. return false;
  151. }
  152. if (md5($this->private_key . 'geetest' . $challenge) != $validate) {
  153. return false;
  154. }
  155. return true;
  156. }
  157. /**
  158. * GET 请求
  159. *
  160. * @param $url
  161. * @return mixed|string
  162. */
  163. private function send_request($url)
  164. {
  165. if (function_exists('curl_exec')) {
  166. $ch = curl_init();
  167. curl_setopt($ch, CURLOPT_URL, $url);
  168. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, self::$connectTimeout);
  169. curl_setopt($ch, CURLOPT_TIMEOUT, self::$socketTimeout);
  170. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  171. $data = curl_exec($ch);
  172. if (curl_errno($ch)) {
  173. $err = sprintf("curl[%s] error[%s]", $url, curl_errno($ch) . ':' . curl_error($ch));
  174. $this->triggerError($err);
  175. }
  176. curl_close($ch);
  177. } else {
  178. $opts = array(
  179. 'http' => array(
  180. 'method' => "GET",
  181. 'timeout' => self::$connectTimeout + self::$socketTimeout,
  182. )
  183. );
  184. $context = stream_context_create($opts);
  185. $data = file_get_contents($url, false, $context);
  186. }
  187. return $data;
  188. }
  189. /**
  190. *
  191. * @param $url
  192. * @param array $postdata
  193. * @return mixed|string
  194. */
  195. private function post_request($url, $postdata = '')
  196. {
  197. if (!$postdata) {
  198. return false;
  199. }
  200. $data = http_build_query($postdata);
  201. if (function_exists('curl_exec')) {
  202. $ch = curl_init();
  203. curl_setopt($ch, CURLOPT_URL, $url);
  204. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  205. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, self::$connectTimeout);
  206. curl_setopt($ch, CURLOPT_TIMEOUT, self::$socketTimeout);
  207. //不可能执行到的代码
  208. if (!$postdata) {
  209. curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
  210. } else {
  211. curl_setopt($ch, CURLOPT_POST, 1);
  212. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  213. }
  214. $data = curl_exec($ch);
  215. if (curl_errno($ch)) {
  216. $err = sprintf("curl[%s] error[%s]", $url, curl_errno($ch) . ':' . curl_error($ch));
  217. $this->triggerError($err);
  218. }
  219. curl_close($ch);
  220. } else {
  221. if ($postdata) {
  222. $opts = array(
  223. 'http' => array(
  224. 'method' => 'POST',
  225. 'header' => "Content-type: application/x-www-form-urlencoded\r\n" . "Content-Length: " . strlen($data) . "\r\n",
  226. 'content' => $data,
  227. 'timeout' => self::$connectTimeout + self::$socketTimeout
  228. )
  229. );
  230. $context = stream_context_create($opts);
  231. $data = file_get_contents($url, false, $context);
  232. }
  233. }
  234. return $data;
  235. }
  236. /**
  237. * 解码随机参数
  238. *
  239. * @param $challenge
  240. * @param $string
  241. * @return int
  242. */
  243. private function decode_response($challenge, $string)
  244. {
  245. if (strlen($string) > 100) {
  246. return 0;
  247. }
  248. $key = array();
  249. $chongfu = array();
  250. $shuzi = array("0" => 1, "1" => 2, "2" => 5, "3" => 10, "4" => 50);
  251. $count = 0;
  252. $res = 0;
  253. $array_challenge = str_split($challenge);
  254. $array_value = str_split($string);
  255. for ($i = 0; $i < strlen($challenge); $i++) {
  256. $item = $array_challenge[$i];
  257. if (in_array($item, $chongfu)) {
  258. continue;
  259. } else {
  260. $value = $shuzi[$count % 5];
  261. array_push($chongfu, $item);
  262. $count++;
  263. $key[$item] = $value;
  264. }
  265. }
  266. for ($j = 0; $j < strlen($string); $j++) {
  267. $res += $key[$array_value[$j]];
  268. }
  269. $res = $res - $this->decodeRandBase($challenge);
  270. return $res;
  271. }
  272. /**
  273. * @param $x_str
  274. * @return int
  275. */
  276. private function get_x_pos_from_str($x_str)
  277. {
  278. if (strlen($x_str) != 5) {
  279. return 0;
  280. }
  281. $sum_val = 0;
  282. $x_pos_sup = 200;
  283. $sum_val = base_convert($x_str, 16, 10);
  284. $result = $sum_val % $x_pos_sup;
  285. $result = ($result < 40) ? 40 : $result;
  286. return $result;
  287. }
  288. /**
  289. * @param $full_bg_index
  290. * @param $img_grp_index
  291. * @return int
  292. */
  293. private function get_failback_pic_ans($full_bg_index, $img_grp_index)
  294. {
  295. $full_bg_name = substr(md5($full_bg_index), 0, 9);
  296. $bg_name = substr(md5($img_grp_index), 10, 9);
  297. $answer_decode = "";
  298. // 通过两个字符串奇数和偶数位拼接产生答案位
  299. for ($i = 0; $i < 9; $i++) {
  300. if ($i % 2 == 0) {
  301. $answer_decode = $answer_decode . $full_bg_name[$i];
  302. } elseif ($i % 2 == 1) {
  303. $answer_decode = $answer_decode . $bg_name[$i];
  304. }
  305. }
  306. $x_decode = substr($answer_decode, 4, 5);
  307. $x_pos = $this->get_x_pos_from_str($x_decode);
  308. return $x_pos;
  309. }
  310. /**
  311. * 输入的两位的随机数字,解码出偏移量
  312. *
  313. * @param $challenge
  314. * @return mixed
  315. */
  316. private function decodeRandBase($challenge)
  317. {
  318. $base = substr($challenge, 32, 2);
  319. $tempArray = array();
  320. for ($i = 0; $i < strlen($base); $i++) {
  321. $tempAscii = ord($base[$i]);
  322. $result = ($tempAscii > 57) ? ($tempAscii - 87) : ($tempAscii - 48);
  323. array_push($tempArray, $result);
  324. }
  325. $decodeRes = $tempArray['0'] * 36 + $tempArray['1'];
  326. return $decodeRes;
  327. }
  328. /**
  329. * @param $err
  330. */
  331. private function triggerError($err)
  332. {
  333. trigger_error($err);
  334. }
  335. }