Repository: GeeTeam/gt-php-sdk Branch: master Commit: 83f25bee5e5e Files: 9 Total size: 30.8 KB Directory structure: gitextract_rznw79nl/ ├── .gitignore ├── README.rst ├── composer.json ├── config/ │ └── config.php ├── lib/ │ └── class.geetestlib.php ├── static/ │ ├── gt.js │ └── login.html └── web/ ├── StartCaptchaServlet.php └── VerifyLoginServlet.php ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .project .buildpath .settings ================================================ FILE: README.rst ================================================ Gt Php SDK =============== 使用 3.1 之前版本SDK的用户如果想更新到3.1以及以后版本请先联系极验客服,因为为了兼容老用户,新的特性需要修改验证设置 本项目是面向服务器端的,具体使用可以参考我们的 `文档 `_ ,客户端相关开发请参考我们的 `前端文档 `_. 开发环境 ---------------- - php5.2 部署架构 --------------- 详见 `部署架构 `__ 前端接口 ------------------- 详见 `前端接口 `__ 宕机回滚 -------------- 详见 `宕机回滚 `__ 文件说明 --------------- - config/config.php 极验ID和KEY配置文件,请在 `极验后台 `__ 申请,进行替换 - lib/class.geetestlib.php 极验库文件(请不要随意改动) - static/login.php 前端展示页面,根据您的需求进行自定义 - web/StartCaptchaServlet.php 根据自己的私钥初始化验证 - web/VerifyLoginServlet.php 根据post参数进行二次验证 - static/gt.js 静态js文件,之前是直接引用http://static.geetest.com/static/tools/gt.js的 常见问题 -------------- 1. 3.1.0之前的老版本SDK,不兼容现在的id和key 发布日志 ----------------- + 3.2.0 - 添加用户标识接口 + 3.1.1 - 统一接口 + 3.1.0 - 添加challenge加密特性,使验证更安全, 老版本更新请先联系管理员 ================================================ FILE: composer.json ================================================ { "name": "gee-team/gt-php-sdk", "description": "GtWeb Php Demo", "authors": [ { "name": "tanxu1993", "email": "tanxu1993@gmail.com" } ], "require": { "php": ">=5.0.0" }, "autoload": { "classmap": [ "lib" ] }, "license": "MIT" } ================================================ FILE: config/config.php ================================================ ================================================ FILE: lib/class.geetestlib.php ================================================ captcha_id = $captcha_id; $this->private_key = $private_key; } /** * 判断极验服务器是否down机 * * @param null $user_id * @return int */ public function pre_process($user_id = null) { $url = "http://api.geetest.com/register.php?gt=" . $this->captcha_id; if (($user_id != null) and (is_string($user_id))) { $url = $url . "&user_id=" . $user_id; } $challenge = $this->send_request($url); if (strlen($challenge) != 32) { $this->failback_process(); return 0; } $this->success_process($challenge); return 1; } /** * @param $challenge */ private function success_process($challenge) { $challenge = md5($challenge . $this->private_key); $result = array( 'success' => 1, 'gt' => $this->captcha_id, 'challenge' => $challenge ); $this->response = $result; } /** * */ private function failback_process() { $rnd1 = md5(rand(0, 100)); $rnd2 = md5(rand(0, 100)); $challenge = $rnd1 . substr($rnd2, 0, 2); $result = array( 'success' => 0, 'gt' => $this->captcha_id, 'challenge' => $challenge ); $this->response = $result; } /** * @return mixed */ public function get_response_str() { return json_encode($this->response); } /** * 返回数组方便扩展 * * @return mixed */ public function get_response() { return $this->response; } /** * 正常模式获取验证结果 * * @param $challenge * @param $validate * @param $seccode * @param null $user_id * @return int */ public function success_validate($challenge, $validate, $seccode, $user_id = null) { if (!$this->check_validate($challenge, $validate)) { return 0; } $data = array( "seccode" => $seccode, "sdk" => self::GT_SDK_VERSION, ); if (($user_id != null) and (is_string($user_id))) { $data["user_id"] = $user_id; } $url = "http://api.geetest.com/validate.php"; $codevalidate = $this->post_request($url, $data); if ($codevalidate == md5($seccode)) { return 1; } else { if ($codevalidate == "false") { return 0; } else { return 0; } } } /** * 宕机模式获取验证结果 * * @param $challenge * @param $validate * @param $seccode * @return int */ public function fail_validate($challenge, $validate, $seccode) { if ($validate) { $value = explode("_", $validate); $ans = $this->decode_response($challenge, $value['0']); $bg_idx = $this->decode_response($challenge, $value['1']); $grp_idx = $this->decode_response($challenge, $value['2']); $x_pos = $this->get_failback_pic_ans($bg_idx, $grp_idx); $answer = abs($ans - $x_pos); if ($answer < 4) { return 1; } else { return 0; } } else { return 0; } } /** * @param $challenge * @param $validate * @return bool */ private function check_validate($challenge, $validate) { if (strlen($validate) != 32) { return false; } if (md5($this->private_key . 'geetest' . $challenge) != $validate) { return false; } return true; } /** * GET 请求 * * @param $url * @return mixed|string */ private function send_request($url) { if (function_exists('curl_exec')) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, self::$connectTimeout); curl_setopt($ch, CURLOPT_TIMEOUT, self::$socketTimeout); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $data = curl_exec($ch); if (curl_errno($ch)) { $err = sprintf("curl[%s] error[%s]", $url, curl_errno($ch) . ':' . curl_error($ch)); $this->triggerError($err); } curl_close($ch); } else { $opts = array( 'http' => array( 'method' => "GET", 'timeout' => self::$connectTimeout + self::$socketTimeout, ) ); $context = stream_context_create($opts); $data = file_get_contents($url, false, $context); } return $data; } /** * * @param $url * @param array $postdata * @return mixed|string */ private function post_request($url, $postdata = '') { if (!$postdata) { return false; } $data = http_build_query($postdata); if (function_exists('curl_exec')) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, self::$connectTimeout); curl_setopt($ch, CURLOPT_TIMEOUT, self::$socketTimeout); //不可能执行到的代码 if (!$postdata) { curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); } else { curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); } $data = curl_exec($ch); if (curl_errno($ch)) { $err = sprintf("curl[%s] error[%s]", $url, curl_errno($ch) . ':' . curl_error($ch)); $this->triggerError($err); } curl_close($ch); } else { if ($postdata) { $opts = array( 'http' => array( 'method' => 'POST', 'header' => "Content-type: application/x-www-form-urlencoded\r\n" . "Content-Length: " . strlen($data) . "\r\n", 'content' => $data, 'timeout' => self::$connectTimeout + self::$socketTimeout ) ); $context = stream_context_create($opts); $data = file_get_contents($url, false, $context); } } return $data; } /** * 解码随机参数 * * @param $challenge * @param $string * @return int */ private function decode_response($challenge, $string) { if (strlen($string) > 100) { return 0; } $key = array(); $chongfu = array(); $shuzi = array("0" => 1, "1" => 2, "2" => 5, "3" => 10, "4" => 50); $count = 0; $res = 0; $array_challenge = str_split($challenge); $array_value = str_split($string); for ($i = 0; $i < strlen($challenge); $i++) { $item = $array_challenge[$i]; if (in_array($item, $chongfu)) { continue; } else { $value = $shuzi[$count % 5]; array_push($chongfu, $item); $count++; $key[$item] = $value; } } for ($j = 0; $j < strlen($string); $j++) { $res += $key[$array_value[$j]]; } $res = $res - $this->decodeRandBase($challenge); return $res; } /** * @param $x_str * @return int */ private function get_x_pos_from_str($x_str) { if (strlen($x_str) != 5) { return 0; } $sum_val = 0; $x_pos_sup = 200; $sum_val = base_convert($x_str, 16, 10); $result = $sum_val % $x_pos_sup; $result = ($result < 40) ? 40 : $result; return $result; } /** * @param $full_bg_index * @param $img_grp_index * @return int */ private function get_failback_pic_ans($full_bg_index, $img_grp_index) { $full_bg_name = substr(md5($full_bg_index), 0, 9); $bg_name = substr(md5($img_grp_index), 10, 9); $answer_decode = ""; // 通过两个字符串奇数和偶数位拼接产生答案位 for ($i = 0; $i < 9; $i++) { if ($i % 2 == 0) { $answer_decode = $answer_decode . $full_bg_name[$i]; } elseif ($i % 2 == 1) { $answer_decode = $answer_decode . $bg_name[$i]; } } $x_decode = substr($answer_decode, 4, 5); $x_pos = $this->get_x_pos_from_str($x_decode); return $x_pos; } /** * 输入的两位的随机数字,解码出偏移量 * * @param $challenge * @return mixed */ private function decodeRandBase($challenge) { $base = substr($challenge, 32, 2); $tempArray = array(); for ($i = 0; $i < strlen($base); $i++) { $tempAscii = ord($base[$i]); $result = ($tempAscii > 57) ? ($tempAscii - 87) : ($tempAscii - 48); array_push($tempArray, $result); } $decodeRes = $tempArray['0'] * 36 + $tempArray['1']; return $decodeRes; } /** * @param $err */ private function triggerError($err) { trigger_error($err); } } ================================================ FILE: static/gt.js ================================================ /* initGeetest 1.0.0 * 用于加载id对应的验证码库,并支持宕机模式 * 暴露 initGeetest 进行验证码的初始化 * 一般不需要用户进行修改 */ (function (global, factory) { "use strict"; if (typeof module === "object" && typeof module.exports === "object") { // CommonJS module.exports = global.document ? factory(global, true) : function (w) { if (!w.document) { throw new Error("Geetest requires a window with a document"); } return factory(w); }; } else { factory(global); } })(typeof window !== "undefined" ? window : this, function (window, noGlobal) { "use strict"; if (typeof window === 'undefined') { throw new Error('Geetest requires browser environment'); } var document = window.document; var Math = window.Math; var head = document.getElementsByTagName("head")[0]; function _Object(obj) { this._obj = obj; } _Object.prototype = { _each: function (process) { var _obj = this._obj; for (var k in _obj) { if (_obj.hasOwnProperty(k)) { process(k, _obj[k]); } } return this; } }; function Config(config) { var self = this; new _Object(config)._each(function (key, value) { self[key] = value; }); } Config.prototype = { api_server: 'api.geetest.com', protocol: 'http://', type_path: '/gettype.php', fallback_config: { slide: { static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"], type: 'slide', slide: '/static/js/geetest.0.0.0.js' }, fullpage: { static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"], type: 'fullpage', fullpage: '/static/js/fullpage.0.0.0.js' } }, _get_fallback_config: function () { var self = this; if (isString(self.type)) { return self.fallback_config[self.type]; } else if (self.new_captcha) { return self.fallback_config.fullpage; } else { return self.fallback_config.slide; } }, _extend: function (obj) { var self = this; new _Object(obj)._each(function (key, value) { self[key] = value; }) } }; var isNumber = function (value) { return (typeof value === 'number'); }; var isString = function (value) { return (typeof value === 'string'); }; var isBoolean = function (value) { return (typeof value === 'boolean'); }; var isObject = function (value) { return (typeof value === 'object' && value !== null); }; var isFunction = function (value) { return (typeof value === 'function'); }; var callbacks = {}; var status = {}; var random = function () { return parseInt(Math.random() * 10000) + (new Date()).valueOf(); }; var loadScript = function (url, cb) { var script = document.createElement("script"); script.charset = "UTF-8"; script.async = true; script.onerror = function () { cb(true); }; var loaded = false; script.onload = script.onreadystatechange = function () { if (!loaded && (!script.readyState || "loaded" === script.readyState || "complete" === script.readyState)) { loaded = true; setTimeout(function () { cb(false); }, 0); } }; script.src = url; head.appendChild(script); }; var normalizeDomain = function (domain) { return domain.replace(/^https?:\/\/|\/$/g, ''); }; var normalizePath = function (path) { path = path.replace(/\/+/g, '/'); if (path.indexOf('/') !== 0) { path = '/' + path; } return path; }; var normalizeQuery = function (query) { if (!query) { return ''; } var q = '?'; new _Object(query)._each(function (key, value) { if (isString(value) || isNumber(value) || isBoolean(value)) { q = q + encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&'; } }); if (q === '?') { q = ''; } return q.replace(/&$/, ''); }; var makeURL = function (protocol, domain, path, query) { domain = normalizeDomain(domain); var url = normalizePath(path) + normalizeQuery(query); if (domain) { url = protocol + domain + url; } return url; }; var load = function (protocol, domains, path, query, cb) { var tryRequest = function (at) { var url = makeURL(protocol, domains[at], path, query); loadScript(url, function (err) { if (err) { if (at >= domains.length - 1) { cb(true); } else { tryRequest(at + 1); } } else { cb(false); } }); }; tryRequest(0); }; var jsonp = function (domains, path, config, callback) { if (isObject(config.getLib)) { config._extend(config.getLib); callback(config); return; } if (config.offline) { callback(config._get_fallback_config()); return; } var cb = "geetest_" + random(); window[cb] = function (data) { if (data.status === 'success') { callback(data.data); } else if (!data.status) { callback(data); } else { callback(config._get_fallback_config()); } window[cb] = undefined; try { delete window[cb]; } catch (e) { } }; load(config.protocol, domains, path, { gt: config.gt, callback: cb }, function (err) { if (err) { callback(config._get_fallback_config()); } }); }; var throwError = function (errorType, config) { var errors = { networkError: '网络错误' }; if (typeof config.onError === 'function') { config.onError(errors[errorType]); } else { throw new Error(errors[errorType]); } }; var detect = function () { return !!window.Geetest; }; if (detect()) { status.slide = "loaded"; } var initGeetest = function (userConfig, callback) { var config = new Config(userConfig); if (userConfig.https) { config.protocol = 'https://'; } else if (!userConfig.protocol) { config.protocol = window.location.protocol + '//'; } jsonp([config.api_server || config.apiserver], config.type_path, config, function (newConfig) { var type = newConfig.type; var init = function () { config._extend(newConfig); callback(new window.Geetest(config)); }; callbacks[type] = callbacks[type] || []; var s = status[type] || 'init'; if (s === 'init') { status[type] = 'loading'; callbacks[type].push(init); load(config.protocol, newConfig.static_servers || newConfig.domains, newConfig[type] || newConfig.path, null, function (err) { if (err) { status[type] = 'fail'; throwError('networkError', config); } else { status[type] = 'loaded'; var cbs = callbacks[type]; for (var i = 0, len = cbs.length; i < len; i = i + 1) { var cb = cbs[i]; if (isFunction(cb)) { cb(); } } callbacks[type] = []; } }); } else if (s === "loaded") { init(); } else if (s === "fail") { throwError('networkError', config); } else if (s === "loading") { callbacks[type].push(init); } }); }; window.initGeetest = initGeetest; return initGeetest; }); ================================================ FILE: static/login.html ================================================ gt-php-sdk-demo

极验验证SDKDemo
















================================================ FILE: web/StartCaptchaServlet.php ================================================ pre_process($user_id); $_SESSION['gtserver'] = $status; $_SESSION['user_id'] = $user_id; echo $GtSdk->get_response_str(); ?> ================================================ FILE: web/VerifyLoginServlet.php ================================================ success_validate($_POST['geetest_challenge'], $_POST['geetest_validate'], $_POST['geetest_seccode'], $user_id); if ($result) { echo '{"status":"success"}'; } else{ echo '{"status":"fail"}'; } }else{ //服务器宕机,走failback模式 if ($GtSdk->fail_validate($_POST['geetest_challenge'],$_POST['geetest_validate'],$_POST['geetest_seccode'])) { echo '{"status":"success"}'; }else{ echo '{"status":"fail"}'; } } ?>