├── Action.php
├── LICENSE
├── Plugin.php
├── README.md
├── images
├── comment_page.png
├── login_page.jpg
└── setting_page.jpg
├── lib
└── class.geetestlib.php
└── static
├── gt.js
└── gt.min.js
/Action.php:
--------------------------------------------------------------------------------
1 | on($this->request->is('do=ajaxResponseCaptchaData'))->ajaxResponseCaptchaData();
15 | }
16 |
17 | public function ajaxResponseCaptchaData()
18 | {
19 | if (!$this->request->isAjax()) {
20 | $this->response->redirect('/');
21 | }
22 |
23 | Typecho_Plugin::factory('Geetest')->responseCaptchaData();
24 | }
25 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 雪山凌狐
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Plugin.php:
--------------------------------------------------------------------------------
1 | end = array(__CLASS__, 'renderCaptcha');
33 |
34 | // 注册用户登录成功钩子
35 | Typecho_Plugin::factory('Widget_User')->loginSucceed = array(__CLASS__, 'verifyCaptcha');
36 |
37 | // 评论钩子
38 | Typecho_Plugin::factory('Widget_Feedback')->comment = array(__CLASS__, 'commentCaptchaVerify');
39 | Typecho_Plugin::factory('Widget_Feedback')->trackback = array(__CLASS__, 'commentCaptchaVerify');
40 | Typecho_Plugin::factory('Widget_XmlRpc')->pingback = array(__CLASS__, 'commentCaptchaVerify');
41 |
42 | // 暴露插件函数(用于在自定义表单中渲染极验验证,以及在自定义逻辑中调用极验验证)
43 | Typecho_Plugin::factory('Geetest')->renderCaptcha = array(__CLASS__, 'renderCaptcha');
44 | Typecho_Plugin::factory('Geetest')->verifyCaptcha = array(__CLASS__, 'verifyCaptcha');
45 | Typecho_Plugin::factory('Geetest')->responseCaptchaData = array(__CLASS__, 'responseCaptchaData');
46 | }
47 |
48 | /**
49 | * 禁用插件方法,如果禁用失败,直接抛出异常
50 | *
51 | * @static
52 | * @access public
53 | * @return void
54 | * @throws Typecho_Plugin_Exception
55 | */
56 | public static function deactivate()
57 | {
58 | Helper::removeAction('geetest');
59 | }
60 |
61 | /**
62 | * 个人用户的配置面板
63 | *
64 | * @access public
65 | * @param Typecho_Widget_Helper_Form $form
66 | * @return void
67 | */
68 | public static function personalConfig(Typecho_Widget_Helper_Form $form)
69 | {
70 | }
71 |
72 | /**
73 | * 获取插件配置面板
74 | *
75 | * @access public
76 | * @param Typecho_Widget_Helper_Form $form 配置面板
77 | * @return void
78 | */
79 | public static function config(Typecho_Widget_Helper_Form $form)
80 | {
81 |
82 | $isOpenGeetestPage = new Typecho_Widget_Helper_Form_Element_Checkbox('isOpenGeetestPage', [
83 | "typechoLogin" => _t('登录界面'),
84 | "typechoComment" => _t('评论页面')
85 | ], array(), _t('开启极验验证码的页面,勾选则开启'), _t('开启评论验证码后需在主题的评论的模板 comments.php 中添加如下字段:'));
86 |
87 | $captchaId = new Typecho_Widget_Helper_Form_Element_Text('captchaId', null, '', _t('公钥(ID):'));
88 | $privateKey = new Typecho_Widget_Helper_Form_Element_Text('privateKey', null, '', _t('私钥(KEY):'));
89 |
90 | $dismode = new Typecho_Widget_Helper_Form_Element_Select('dismod', array(
91 | 'float' => '浮动式(float)',
92 | 'embed' => '嵌入式(embed)',
93 | 'popup' => '弹出框(popup)'
94 | ), 'float', _t('展现形式:'));
95 |
96 | $cdnUrl = new Typecho_Widget_Helper_Form_Element_Text('cdnUrl', null, '', _t('引入JS的CDN加速地址:'), _t('注意使用 https 协议
留空默认引入本地/static/gt.js文件,不知道的可留空'));
97 |
98 | $debugMode = new Typecho_Widget_Helper_Form_Element_Select('debugMode', array(
99 | '0' => '关闭',
100 | '1' => '开启'
101 | ), '0', _t('调试模式:'), _t('开启时,不会禁用提交按钮,用于测试插件是否生效。'));
102 |
103 | $form->addInput($isOpenGeetestPage);
104 | $form->addInput($captchaId);
105 | $form->addInput($privateKey);
106 | $form->addInput($dismode);
107 | $form->addInput($cdnUrl);
108 | $form->addInput($debugMode);
109 | }
110 |
111 | /**
112 | * 响应验证码数据
113 | */
114 | public static function responseCaptchaData()
115 | {
116 | @session_start();
117 |
118 | $pluginOptions = Helper::options()->plugin('Geetest');
119 | $geetestSdk = new GeetestLib($pluginOptions->captchaId, $pluginOptions->privateKey);
120 |
121 | $widgetRequest = Typecho_Widget::widget('Widget_Options')->request;
122 | $agent = $widgetRequest->getAgent();
123 |
124 | $data = array(
125 | 'user_id' => rand(1000, 9999),
126 | 'client_type' => self::isMobile($agent) ? 'h5' : 'web',
127 | 'ip_address' => $widgetRequest->getIp()
128 | );
129 |
130 | $_SESSION['gt_server_ok'] = $geetestSdk->pre_process($data, 1);
131 | $_SESSION['gt_user_id'] = $data['user_id'];
132 |
133 | echo $geetestSdk->get_response_str();
134 | }
135 |
136 | /**
137 | * 渲染后台登陆 验证码
138 | */
139 | public static function renderCaptcha()
140 | {
141 | // 判断是否登录页面
142 | $widgetOptions = Typecho_Widget::widget('Widget_Options');
143 | $widgetRequest = $widgetOptions->request;
144 | $currentRequestUrl = $widgetRequest->getRequestUrl();
145 | if (!stripos($currentRequestUrl, 'login.php')) {
146 | return;
147 | }
148 | // 取出插件的配置
149 | $pluginOptions = Helper::options()->plugin('Geetest');
150 | $isOpenGeetestPage = $pluginOptions->isOpenGeetestPage;
151 | // 判断是否开启登陆页的验证码
152 | if (!in_array("typechoLogin", $isOpenGeetestPage)) {
153 | return;
154 | }
155 | $cdnUrl = ($pluginOptions->cdnUrl ? $pluginOptions->cdnUrl : Helper::options()->pluginUrl . '/Geetest/static/gt.min.js');
156 | $debugMode = (bool)($pluginOptions->debugMode);
157 |
158 | $disableButtonJs = '';
159 | $disableSubmitJs = '';
160 | if (!$debugMode) {
161 | $disableButtonJs = 'jqFormSubmit.attr({disabled:true}).addClass("gt-btn-disabled");';
162 | $disableSubmitJs = <<
176 | #gt-captcha { line-height: 44px; }
177 | #gt-captcha .waiting { background-color: #e8e8e8; color: #4d4d4d; }
178 | .gt-btn-disabled { background-color: #a3b7c1!important; color: #fff!important; cursor: no-drop!important; }
179 |
180 |
181 |
182 |
231 | EOF;
232 | }
233 |
234 | /**
235 | * 渲染评论验证码
236 | * @throws Typecho_Plugin_Exception
237 | */
238 | public static function commentCaptchaRender() {
239 | //判断插件是否激活
240 | $options = Typecho_Widget::widget('Widget_Options');
241 | if (!isset($options->plugins['activated']['Geetest'])) {
242 | echo '极验评论验证码插件未激活
';
243 | return;
244 | }
245 |
246 | // 取出插件的配置
247 | $pluginOptions = Helper::options()->plugin('Geetest');
248 | $isOpenGeetestPage = $pluginOptions->isOpenGeetestPage;
249 | //判断是否开启评论页的验证码
250 | if (!in_array("typechoComment", $isOpenGeetestPage)) {
251 | return;
252 | }
253 | $cdnUrl = ($pluginOptions->cdnUrl ? $pluginOptions->cdnUrl : Helper::options()->pluginUrl . '/Geetest/static/gt.min.js');
254 | $debugMode = (bool)($pluginOptions->debugMode);
255 |
256 | $disableButtonJs = '';
257 | $disableSubmitJs = '';
258 | if (!$debugMode) {
259 | $disableButtonJs = '$("#sub_btn").attr({disabled:true}).addClass("gt-btn-disabled");';
260 | $disableSubmitJs = <<
274 | #gt-captcha { line-height: 44px; }
275 | .gt-btn-disabled { background-color: #a3b7c1!important; color: #fff!important; cursor: no-drop!important; }
276 |
277 |
278 |
279 |
324 | EOF;
325 | }
326 |
327 | /**
328 | * 评论验证码 校验
329 | * @access public
330 | * @param array $comment 评论内容
331 | */
332 | public static function commentCaptchaVerify($comment)
333 | {
334 | // 取出插件的配置
335 | $pluginOptions = Helper::options()->plugin('Geetest');
336 | $isOpenGeetestPage = $pluginOptions->isOpenGeetestPage;
337 | //判断是否开启评论页的验证码
338 | if (in_array("typechoComment", $isOpenGeetestPage)) {
339 | if (!self::_verifyCaptcha()) {
340 | echo "";
341 | exit();
342 | }
343 | }
344 | return $comment;
345 |
346 | }
347 |
348 | /**
349 | * 后台登陆验证码 校验
350 | */
351 | public static function verifyCaptcha()
352 | {
353 | //取出插件的配置
354 | $pluginOptions = Helper::options()->plugin('Geetest');
355 | $isOpenGeetestPage = $pluginOptions->isOpenGeetestPage;
356 | //判断是否开启评论页的验证码
357 | if (in_array("typechoLogin", $isOpenGeetestPage)) {
358 | if (!self::_verifyCaptcha()) {
359 | Typecho_Widget::widget('Widget_Notice')->set(_t('验证码错误'), 'error');
360 | Typecho_Widget::widget('Widget_User')->logout();
361 | Typecho_Widget::widget('Widget_Options')->response->goBack();
362 | }
363 | }
364 | }
365 |
366 | /**
367 | * 校验验证码 方法
368 | *
369 | * @return int
370 | */
371 | private static function _verifyCaptcha()
372 | {
373 | // 如果插件渲染失败,则默认验证不通过
374 | if (!isset($_POST['geetest_challenge']) || !isset($_POST['geetest_validate']) || !isset($_POST['geetest_seccode'])) {
375 | return 0;
376 | }
377 |
378 | @session_start();
379 |
380 | $pluginOptions = Helper::options()->plugin('Geetest');
381 | $geetestSdk = new GeetestLib($pluginOptions->captchaId, $pluginOptions->privateKey);
382 |
383 | if (!empty($_SESSION['gt_server_ok'])) {
384 |
385 | $widgetRequest = Typecho_Widget::widget('Widget_Options')->request;
386 | $agent = $widgetRequest->getAgent();
387 | $clientType = self::isMobile($agent) ? 'h5' : 'web';
388 | $ipAddress = $widgetRequest->getIp();
389 |
390 | $data = array(
391 | 'user_id' => $_SESSION['gt_user_id'],
392 | 'client_type' => $clientType,
393 | 'ip_address' => $ipAddress
394 | );
395 |
396 | return $geetestSdk->success_validate($_POST['geetest_challenge'], $_POST['geetest_validate'], $_POST['geetest_seccode'], $data);
397 | }
398 |
399 | return $geetestSdk->fail_validate($_POST['geetest_challenge'], $_POST['geetest_validate'], $_POST['geetest_seccode']);
400 | }
401 |
402 | /**
403 | * isMobile
404 | *
405 | * @static
406 | * @access public
407 | * @return boolean
408 | */
409 | public static function isMobile($userAgent)
410 | {
411 | return preg_match('/android.+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i', $userAgent) || preg_match('/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i', substr($userAgent, 0, 4));
412 | }
413 |
414 | }
415 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Geetest for Typecho
2 |
3 | 极验验证插件,用于用户登录、用户评论时使用极验提供的滑动验证码,适配了Material主题
4 |
5 | ### 更新说明
6 | 保留原插件的登陆验证功能,新增评论验证功能
7 |
8 | ### 使用方法
9 |
10 | #### 1 下载激活插件
11 | 下载插件后,解压,将文件夹名称改为 Geetest,上传到 /usr/plugins 目录下,在插件面板启用插件并配置即可使用;
12 |
13 | 或者直接在 Typecho 的插件目录下执行如下命令:
14 | ```
15 | cd typechoPath/usr/plugins
16 | git clone https://github.com/noisky/typecho-plugin-geetest.git Geetest
17 | ```
18 |
19 | #### 2 配置插件
20 | 极验验证码的 ID 和 KEY 需要到极验官网 `https://www.geetest.com/` 获取;
21 |
22 | 注册、创建应用的时候,基础版是免费的;
23 |
24 | 如需开启评论验证码,则需要在你的主题评论模板 `comment.php` 中的任意一行添加如下代码:
25 | ```
26 |
27 |
28 | ```
29 | 该功能需要JQuery插件支持,如果主题已经集成则不用引入该插件。
30 |
31 | **插件设置**
32 |
33 | 
34 |
35 | **后台登录验证码**
36 |
37 | 
38 |
39 | **评论验证码**
40 |
41 | 
42 |
43 | ### Thanks
44 |
45 | @zhb127
46 |
47 | @xueshanlinghu
48 |
--------------------------------------------------------------------------------
/images/comment_page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noisky/typecho-plugin-geetest/3cd9d5e855504abd319d5fd110791c8b60d6f0a6/images/comment_page.png
--------------------------------------------------------------------------------
/images/login_page.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noisky/typecho-plugin-geetest/3cd9d5e855504abd319d5fd110791c8b60d6f0a6/images/login_page.jpg
--------------------------------------------------------------------------------
/images/setting_page.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noisky/typecho-plugin-geetest/3cd9d5e855504abd319d5fd110791c8b60d6f0a6/images/setting_page.jpg
--------------------------------------------------------------------------------
/lib/class.geetestlib.php:
--------------------------------------------------------------------------------
1 | captcha_id = $captcha_id;
18 | $this->private_key = $private_key;
19 | }
20 |
21 | /**
22 | * 判断极验服务器是否down机
23 | *
24 | * @param array $data
25 | * @return int
26 | */
27 | public function pre_process($param, $new_captcha=1) {
28 | $data = array('gt'=>$this->captcha_id,
29 | 'new_captcha'=>$new_captcha
30 | );
31 | $data = array_merge($data,$param);
32 | $query = http_build_query($data);
33 | $url = "http://api.geetest.com/register.php?" . $query;
34 | $challenge = $this->send_request($url);
35 | if (strlen($challenge) != 32) {
36 | $this->failback_process();
37 | return 0;
38 | }
39 | $this->success_process($challenge);
40 | return 1;
41 | }
42 |
43 | /**
44 | * @param $challenge
45 | */
46 | private function success_process($challenge) {
47 | $challenge = md5($challenge . $this->private_key);
48 | $result = array(
49 | 'success' => 1,
50 | 'gt' => $this->captcha_id,
51 | 'challenge' => $challenge,
52 | 'new_captcha'=>1
53 | );
54 | $this->response = $result;
55 | }
56 |
57 | /**
58 | *
59 | */
60 | private function failback_process() {
61 | $rnd1 = md5(rand(0, 100));
62 | $rnd2 = md5(rand(0, 100));
63 | $challenge = $rnd1 . substr($rnd2, 0, 2);
64 | $result = array(
65 | 'success' => 0,
66 | 'gt' => $this->captcha_id,
67 | 'challenge' => $challenge,
68 | 'new_captcha'=>1
69 | );
70 | $this->response = $result;
71 | }
72 |
73 | /**
74 | * @return mixed
75 | */
76 | public function get_response_str() {
77 | return json_encode($this->response);
78 | }
79 |
80 | /**
81 | * 返回数组方便扩展
82 | *
83 | * @return mixed
84 | */
85 | public function get_response() {
86 | return $this->response;
87 | }
88 |
89 | /**
90 | * 正常模式获取验证结果
91 | *
92 | * @param string $challenge
93 | * @param string $validate
94 | * @param string $seccode
95 | * @param array $param
96 | * @return int
97 | */
98 | public function success_validate($challenge, $validate, $seccode,$param, $json_format=1) {
99 | if (!$this->check_validate($challenge, $validate)) {
100 | return 0;
101 | }
102 | $query = array(
103 | "seccode" => $seccode,
104 | "timestamp"=>time(),
105 | "challenge"=>$challenge,
106 | "captchaid"=>$this->captcha_id,
107 | "json_format"=>$json_format,
108 | "sdk" => self::GT_SDK_VERSION
109 | );
110 | $query = array_merge($query,$param);
111 | $url = "http://api.geetest.com/validate.php";
112 | $codevalidate = $this->post_request($url, $query);
113 | $obj = json_decode($codevalidate,true);
114 | if ($obj === false){
115 | return 0;
116 | }
117 | if ($obj['seccode'] == md5($seccode)) {
118 | return 1;
119 | } else {
120 | return 0;
121 | }
122 | }
123 |
124 | /**
125 | * 宕机模式获取验证结果
126 | *
127 | * @param $challenge
128 | * @param $validate
129 | * @param $seccode
130 | * @return int
131 | */
132 | public function fail_validate($challenge, $validate, $seccode) {
133 | if(md5($challenge) == $validate){
134 | return 1;
135 | }else{
136 | return 0;
137 | }
138 | }
139 |
140 | /**
141 | * @param $challenge
142 | * @param $validate
143 | * @return bool
144 | */
145 | private function check_validate($challenge, $validate) {
146 | if (strlen($validate) != 32) {
147 | return false;
148 | }
149 | if (md5($this->private_key . 'geetest' . $challenge) != $validate) {
150 | return false;
151 | }
152 |
153 | return true;
154 | }
155 |
156 | /**
157 | * GET 请求
158 | *
159 | * @param $url
160 | * @return mixed|string
161 | */
162 | private function send_request($url) {
163 |
164 | if (function_exists('curl_exec')) {
165 | $ch = curl_init();
166 | curl_setopt($ch, CURLOPT_URL, $url);
167 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, self::$connectTimeout);
168 | curl_setopt($ch, CURLOPT_TIMEOUT, self::$socketTimeout);
169 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
170 | $curl_errno = curl_errno($ch);
171 | $data = curl_exec($ch);
172 | curl_close($ch);
173 | if ($curl_errno >0) {
174 | return 0;
175 | }else{
176 | return $data;
177 | }
178 | } else {
179 | $opts = array(
180 | 'http' => array(
181 | 'method' => "GET",
182 | 'timeout' => self::$connectTimeout + self::$socketTimeout,
183 | )
184 | );
185 | $context = stream_context_create($opts);
186 | $data = @file_get_contents($url, false, $context);
187 | if($data){
188 | return $data;
189 | }else{
190 | return 0;
191 | }
192 | }
193 | }
194 |
195 | /**
196 | *
197 | * @param $url
198 | * @param array $postdata
199 | * @return mixed|string
200 | */
201 | private function post_request($url, $postdata = '') {
202 | if (!$postdata) {
203 | return false;
204 | }
205 |
206 | $data = http_build_query($postdata);
207 | if (function_exists('curl_exec')) {
208 | $ch = curl_init();
209 | curl_setopt($ch, CURLOPT_URL, $url);
210 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
211 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, self::$connectTimeout);
212 | curl_setopt($ch, CURLOPT_TIMEOUT, self::$socketTimeout);
213 |
214 | //不可能执行到的代码
215 | if (!$postdata) {
216 | curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
217 | } else {
218 | curl_setopt($ch, CURLOPT_POST, 1);
219 | curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
220 | }
221 | $data = curl_exec($ch);
222 |
223 | if (curl_errno($ch)) {
224 | $err = sprintf("curl[%s] error[%s]", $url, curl_errno($ch) . ':' . curl_error($ch));
225 | $this->triggerError($err);
226 | }
227 |
228 | curl_close($ch);
229 | } else {
230 | if ($postdata) {
231 | $opts = array(
232 | 'http' => array(
233 | 'method' => 'POST',
234 | 'header' => "Content-type: application/x-www-form-urlencoded\r\n" . "Content-Length: " . strlen($data) . "\r\n",
235 | 'content' => $data,
236 | 'timeout' => self::$connectTimeout + self::$socketTimeout
237 | )
238 | );
239 | $context = stream_context_create($opts);
240 | $data = file_get_contents($url, false, $context);
241 | }
242 | }
243 |
244 | return $data;
245 | }
246 |
247 |
248 |
249 | /**
250 | * @param $err
251 | */
252 | private function triggerError($err) {
253 | trigger_error($err);
254 | }
255 | }
256 |
--------------------------------------------------------------------------------
/static/gt.js:
--------------------------------------------------------------------------------
1 | /* initGeetest 1.0.0
2 | * 用于加载id对应的验证码库,并支持宕机模式
3 | * 暴露 initGeetest 进行验证码的初始化
4 | * 一般不需要用户进行修改
5 | */
6 | (function (global, factory) {
7 | "use strict";
8 | if (typeof module === "object" && typeof module.exports === "object") {
9 | // CommonJS
10 | module.exports = global.document ?
11 | factory(global, true) :
12 | function (w) {
13 | if (!w.document) {
14 | throw new Error("Geetest requires a window with a document");
15 | }
16 | return factory(w);
17 | };
18 | } else {
19 | factory(global);
20 | }
21 | })(typeof window !== "undefined" ? window : this, function (window, noGlobal) {
22 | "use strict";
23 | if (typeof window === 'undefined') {
24 | throw new Error('Geetest requires browser environment');
25 | }
26 | var document = window.document;
27 | var Math = window.Math;
28 | var head = document.getElementsByTagName("head")[0];
29 |
30 | function _Object(obj) {
31 | this._obj = obj;
32 | }
33 |
34 | _Object.prototype = {
35 | _each: function (process) {
36 | var _obj = this._obj;
37 | for (var k in _obj) {
38 | if (_obj.hasOwnProperty(k)) {
39 | process(k, _obj[k]);
40 | }
41 | }
42 | return this;
43 | }
44 | };
45 | function Config(config) {
46 | var self = this;
47 | new _Object(config)._each(function (key, value) {
48 | self[key] = value;
49 | });
50 | }
51 |
52 | Config.prototype = {
53 | api_server: 'api.geetest.com',
54 | protocol: 'http://',
55 | type_path: '/gettype.php',
56 | fallback_config: {
57 | slide: {
58 | static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
59 | type: 'slide',
60 | slide: '/static/js/geetest.0.0.0.js'
61 | },
62 | fullpage: {
63 | static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
64 | type: 'fullpage',
65 | fullpage: '/static/js/fullpage.0.0.0.js'
66 | }
67 | },
68 | _get_fallback_config: function () {
69 | var self = this;
70 | if (isString(self.type)) {
71 | return self.fallback_config[self.type];
72 | } else if (self.new_captcha) {
73 | return self.fallback_config.fullpage;
74 | } else {
75 | return self.fallback_config.slide;
76 | }
77 | },
78 | _extend: function (obj) {
79 | var self = this;
80 | new _Object(obj)._each(function (key, value) {
81 | self[key] = value;
82 | })
83 | }
84 | };
85 | var isNumber = function (value) {
86 | return (typeof value === 'number');
87 | };
88 | var isString = function (value) {
89 | return (typeof value === 'string');
90 | };
91 | var isBoolean = function (value) {
92 | return (typeof value === 'boolean');
93 | };
94 | var isObject = function (value) {
95 | return (typeof value === 'object' && value !== null);
96 | };
97 | var isFunction = function (value) {
98 | return (typeof value === 'function');
99 | };
100 | var callbacks = {};
101 | var status = {};
102 | var random = function () {
103 | return parseInt(Math.random() * 10000) + (new Date()).valueOf();
104 | };
105 | var loadScript = function (url, cb) {
106 | var script = document.createElement("script");
107 | script.charset = "UTF-8";
108 | script.async = true;
109 | script.onerror = function () {
110 | cb(true);
111 | };
112 | var loaded = false;
113 | script.onload = script.onreadystatechange = function () {
114 | if (!loaded &&
115 | (!script.readyState ||
116 | "loaded" === script.readyState ||
117 | "complete" === script.readyState)) {
118 |
119 | loaded = true;
120 | setTimeout(function () {
121 | cb(false);
122 | }, 0);
123 | }
124 | };
125 | script.src = url;
126 | head.appendChild(script);
127 | };
128 | var normalizeDomain = function (domain) {
129 | return domain.replace(/^https?:\/\/|\/$/g, '');
130 | };
131 | var normalizePath = function (path) {
132 | path = path.replace(/\/+/g, '/');
133 | if (path.indexOf('/') !== 0) {
134 | path = '/' + path;
135 | }
136 | return path;
137 | };
138 | var normalizeQuery = function (query) {
139 | if (!query) {
140 | return '';
141 | }
142 | var q = '?';
143 | new _Object(query)._each(function (key, value) {
144 | if (isString(value) || isNumber(value) || isBoolean(value)) {
145 | q = q + encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';
146 | }
147 | });
148 | if (q === '?') {
149 | q = '';
150 | }
151 | return q.replace(/&$/, '');
152 | };
153 | var makeURL = function (protocol, domain, path, query) {
154 | domain = normalizeDomain(domain);
155 |
156 | var url = normalizePath(path) + normalizeQuery(query);
157 | if (domain) {
158 | url = protocol + domain + url;
159 | }
160 |
161 | return url;
162 | };
163 | var load = function (protocol, domains, path, query, cb) {
164 | var tryRequest = function (at) {
165 |
166 | var url = makeURL(protocol, domains[at], path, query);
167 | loadScript(url, function (err) {
168 | if (err) {
169 | if (at >= domains.length - 1) {
170 | cb(true);
171 | } else {
172 | tryRequest(at + 1);
173 | }
174 | } else {
175 | cb(false);
176 | }
177 | });
178 | };
179 | tryRequest(0);
180 | };
181 | var jsonp = function (domains, path, config, callback) {
182 | if (isObject(config.getLib)) {
183 | config._extend(config.getLib);
184 | callback(config);
185 | return;
186 | }
187 | if (config.offline) {
188 | callback(config._get_fallback_config());
189 | return;
190 | }
191 | var cb = "geetest_" + random();
192 | window[cb] = function (data) {
193 | if (data.status === 'success') {
194 | callback(data.data);
195 | } else if (!data.status) {
196 | callback(data);
197 | } else {
198 | callback(config._get_fallback_config());
199 | }
200 | window[cb] = undefined;
201 | try {
202 | delete window[cb];
203 | } catch (e) {
204 | }
205 | };
206 | load(config.protocol, domains, path, {
207 | gt: config.gt,
208 | callback: cb
209 | }, function (err) {
210 | if (err) {
211 | callback(config._get_fallback_config());
212 | }
213 | });
214 | };
215 | var throwError = function (errorType, config) {
216 | var errors = {
217 | networkError: '网络错误'
218 | };
219 | if (typeof config.onError === 'function') {
220 | config.onError(errors[errorType]);
221 | } else {
222 | throw new Error(errors[errorType]);
223 | }
224 | };
225 | var detect = function () {
226 | return !!window.Geetest;
227 | };
228 | if (detect()) {
229 | status.slide = "loaded";
230 | }
231 | var initGeetest = function (userConfig, callback) {
232 | var config = new Config(userConfig);
233 | if (userConfig.https) {
234 | config.protocol = 'https://';
235 | } else if (!userConfig.protocol) {
236 | config.protocol = window.location.protocol + '//';
237 | }
238 | jsonp([config.api_server || config.apiserver], config.type_path, config, function (newConfig) {
239 | var type = newConfig.type;
240 | var init = function () {
241 | config._extend(newConfig);
242 | callback(new window.Geetest(config));
243 | };
244 | callbacks[type] = callbacks[type] || [];
245 | var s = status[type] || 'init';
246 | if (s === 'init') {
247 | status[type] = 'loading';
248 | callbacks[type].push(init);
249 | load(config.protocol, newConfig.static_servers || newConfig.domains, newConfig[type] || newConfig.path, null, function (err) {
250 | if (err) {
251 | status[type] = 'fail';
252 | throwError('networkError', config);
253 | } else {
254 | status[type] = 'loaded';
255 | var cbs = callbacks[type];
256 | for (var i = 0, len = cbs.length; i < len; i = i + 1) {
257 | var cb = cbs[i];
258 | if (isFunction(cb)) {
259 | cb();
260 | }
261 | }
262 | callbacks[type] = [];
263 | }
264 | });
265 | } else if (s === "loaded") {
266 | init();
267 | } else if (s === "fail") {
268 | throwError('networkError', config);
269 | } else if (s === "loading") {
270 | callbacks[type].push(init);
271 | }
272 | });
273 | };
274 | window.initGeetest = initGeetest;
275 | return initGeetest;
276 | });
277 |
278 |
--------------------------------------------------------------------------------
/static/gt.min.js:
--------------------------------------------------------------------------------
1 | (function(b,a){if(typeof module==="object"&&typeof module.exports==="object"){module.exports=b.document?a(b,true):function(c){if(!c.document){throw new Error("Geetest requires a window with a document")}return a(c)}}else{a(b)}})(typeof window!=="undefined"?window:this,function(g,t){if(typeof g==="undefined"){throw new Error("Geetest requires browser environment")}var o=g.document;var i=g.Math;var d=o.getElementsByTagName("head")[0];function e(z){this._obj=z}e.prototype={_each:function(A){var B=this._obj;for(var z in B){if(B.hasOwnProperty(z)){A(z,B[z])}}return this}};function h(A){var z=this;new e(A)._each(function(B,C){z[B]=C})}h.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 z=this;if(m(z.type)){return z.fallback_config[z.type]}else{if(z.new_captcha){return z.fallback_config.fullpage}else{return z.fallback_config.slide}}},_extend:function(A){var z=this;new e(A)._each(function(B,C){z[B]=C})}};var n=function(z){return(typeof z==="number")};var m=function(z){return(typeof z==="string")};var j=function(z){return(typeof z==="boolean")};var k=function(z){return(typeof z==="object"&&z!==null)};var c=function(z){return(typeof z==="function")};var x={};var q={};var a=function(){return parseInt(i.random()*10000)+(new Date()).valueOf()};var l=function(C,z){var A=o.createElement("script");A.charset="UTF-8";A.async=true;A.onerror=function(){z(true)};var B=false;A.onload=A.onreadystatechange=function(){if(!B&&(!A.readyState||"loaded"===A.readyState||"complete"===A.readyState)){B=true;setTimeout(function(){z(false)},0)}};A.src=C;d.appendChild(A)};var r=function(z){return z.replace(/^https?:\/\/|\/$/g,"")};var w=function(z){z=z.replace(/\/+/g,"/");if(z.indexOf("/")!==0){z="/"+z}return z};var v=function(A){if(!A){return""}var z="?";new e(A)._each(function(B,C){if(m(C)||n(C)||j(C)){z=z+encodeURIComponent(B)+"="+encodeURIComponent(C)+"&"}});if(z==="?"){z=""}return z.replace(/&$/,"")};var p=function(D,B,C,A){B=r(B);var z=w(C)+v(A);if(B){z=D+B+z}return z};var f=function(E,A,D,C,z){var B=function(F){var G=p(E,A[F],D,C);l(G,function(H){if(H){if(F>=A.length-1){z(true)}else{B(F+1)}}else{z(false)}})};B(0)};var y=function(A,C,B,D){if(k(B.getLib)){B._extend(B.getLib);D(B);return}if(B.offline){D(B._get_fallback_config());return}var z="geetest_"+a();g[z]=function(E){if(E.status==="success"){D(E.data)}else{if(!E.status){D(E)}else{D(B._get_fallback_config())}}g[z]=undefined;try{delete g[z]}catch(F){}};f(B.protocol,A,C,{gt:B.gt,callback:z},function(E){if(E){D(B._get_fallback_config())}})};var s=function(A,z){var B={networkError:"网络错误"};if(typeof z.onError==="function"){z.onError(B[A])}else{throw new Error(B[A])}};var u=function(){return !!g.Geetest};if(u()){q.slide="loaded"}var b=function(A,B){var z=new h(A);if(A.https){z.protocol="https://"}else{if(!A.protocol){z.protocol=g.location.protocol+"//"}}y([z.api_server||z.apiserver],z.type_path,z,function(C){var E=C.type;var F=function(){z._extend(C);B(new g.Geetest(z))};x[E]=x[E]||[];var D=q[E]||"init";if(D==="init"){q[E]="loading";x[E].push(F);f(z.protocol,C.static_servers||C.domains,C[E]||C.path,null,function(K){if(K){q[E]="fail";s("networkError",z)}else{q[E]="loaded";var I=x[E];for(var J=0,H=I.length;J