├── .gitignore
├── CHANGELOG.md
├── README.md
├── demo
├── app.js
├── gt.js
└── login.html
├── gt-sdk.js
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | npm-debug.log
3 | node_modules
4 | .DS_Store
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 4.1.2
2 |
3 | 1. 将gt.js转为本地存储,利用多CDN,增加静态文件加载的可靠性。
4 |
5 | # 4.1.0
6 |
7 | 1. 重命名配置参数 `apiServer` 为 `api_server`
8 | 2. 完善 README
9 |
10 | # 4.0.0
11 |
12 | 1. 完善 `register` 和 `validate` 接口提供的 Promise 风格的异步处理形式
13 | 2. 对于使用回调(handler)形式,符合了 NodeJS 中对错误的通知(即回调的第一个参数为err)
14 | 3. 修改demo,使用了 Promise 风格和回调风格,同时强调了 failback 流程
15 |
16 | # 3.0.0
17 |
18 | 1. 鉴于遇到很多用户对 `publicKey` 和 `privateKey` 容易搞混淆,先将其改名为 `geetest_id` 和 `geetest_key` 。因此,和以前的版本无法兼容,所以修改主版本号。
19 |
20 | # 2.2.1
21 |
22 | 1. 优化移动端自实现弹出式验证demo
23 |
24 |
25 | # 2.2.0
26 |
27 | 1. 新增移动端自实现弹出式验证demo
28 |
29 | # 2.1.1
30 |
31 | 1. 修复register协议名称未添加bug
32 |
33 | # 2.1.0
34 |
35 | 1. 添加promise形式回调
36 |
37 | # 2.0.0
38 |
39 | 1. 增加failback模式
40 | 2. 修改了接口register的回调参数
41 | 3. 实例化传参改为传入一个配置对象
42 |
43 | 具体请参照用法说明:[README](README.md)
44 |
45 | # Update in 1.0.0 更新内容
46 |
47 | 1.添加多实例验证码创建方法
48 | 2.更新demo
49 |
50 | # Update in 0.5.4 更新内容
51 | 对challenge进行md5操作,增加安全性。
52 |
53 | # Update in 0.5.1 更新内容
54 | 将sdk里面的bodyParser函数去掉,简化了各接口的内部实现,接口使用不变。
55 | 添加了示例,并将带有failback功能的脚本统一放到了gt.js文件中,提供一个initGeetest的接口,用法参考示例
56 |
57 | # Update in 0.4.0 更新内容
58 | Use recommended error handler method: callback(err, result).
59 | 规范了错误处理,所有的回调函数现在均遵循node规范,以callback(err, result)的形式,因此您需要按照新的方法修改您的代码以正常运行。
60 |
61 | # Update in 0.3.3 更新内容
62 | You can modify the api server address
63 |
64 | 你现在可以修改APIServer的地址了
65 |
66 | # Update in 0.3.1 更新内容
67 | Use register api to check the server status, if it return false, switch to local captcha manually
68 |
69 | 现在可以用register接口作为检查服务器是否正常的方法,如果register返回false则切换为本地验证码
70 |
71 | # What's New in 0.2.0 更新内容
72 | Pass public key to use `register` API, optional right now, if you don't use it now, the captcha might broke some time in the future
73 |
74 | 将你的Public Key作为第二个参数传入,此参数现在为可选参数,只有传入才能够使用`register`接口。Register接口可能在将来成为必须的接口
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **注意:3.x.x之后修改了所传入的字段名称,将 `publicKey` 和 `privateKey` 修改为 `geetest_id` 和 `geetest_key` ,升级会造成不兼容问题,请各位注意在升级前修改相应的字段名称,新用户无须关注此修改**
2 |
3 | **我们现在将 https://static.geetest.com/static/tools/gt.js 文件放在了本地,目的在于利用多CDN,尽可能保证静态文件的加载。**
4 |
5 | # Install 安装
6 |
7 | ```
8 | npm install geetest --save
9 | ```
10 |
11 | # 运行DEMO
12 |
13 | ```shell
14 | cd Project
15 | git clone https://github.com/GeeTeam/gt-node-sdk.git
16 | cd gt-node-sdk
17 | npm install
18 | npm start
19 | // 最后请打开浏览器访问localhost:8080
20 | // 了解sdk的使用方式请查阅demo目录下的app.js文件
21 | ```
22 |
23 | # 使用说明
24 |
25 | sdk 提供 `Geetest` 构造函数,实例化时需要传入一个配置对象。
26 |
27 | 配置对象的字段如下:
28 |
29 | - `geetest_id`:验证公钥,**必须**
30 | - `geetest_key`:验证私钥,**必须**
31 | - `protocol`:与极验服务器交互时使用的协议,默认为 `http://`,**可选**
32 | - `api_server`:针对私有化用户提供对默认的 `api.geetest.com` 进行修改,普通用户无需关注此选项,**可选**
33 |
34 | `geetest_id` 和 `geetest_key` 申请地址:[account.geetest.com](http://account.geetest.com/)
35 |
36 | 申请后,初始化 `Geetest`:
37 |
38 | ```js
39 | var Geetest = require('geetest');
40 |
41 | var captcha = new Geetest({
42 | geetest_id: 'xxx', // 将xxx替换为您申请的id
43 | geetest_key: 'xxx', // 将xxx替换为您申请的key
44 | });
45 | ```
46 |
47 | 上述 `Geetest` 的实例 `captcha` 提供两个方法:
48 |
49 | ## `register(callback)`
50 |
51 | ```js
52 | // 回调形式
53 | captcha.register(function (err, data) {
54 |
55 | // err 表示发生错误
56 | if (err) {
57 | console.error(err);
58 | return;
59 | }
60 |
61 | // data 为一个对象,包含 gt, challenge, success, new_captcha 字段
62 | // success 为 1 表示正常模式,为 0 表示宕机模式(failback, fallback)
63 | var body = {
64 | gt: data.geetest_id,
65 | challenge: data.challenge,
66 | success: data.success
67 | };
68 |
69 | // 将 body 发送给前端...
70 | });
71 |
72 | // Promise 形式
73 | captcha.register().then(function (data) {
74 |
75 | // data 为一个对象,包含 gt, challenge, success, new_captcha 字段
76 | // success 为 1 表示正常模式,为 0 表示宕机模式(failback, fallback)
77 | var body = {
78 | gt: data.geetest_id,
79 | challenge: data.challenge,
80 | success: data.success
81 | };
82 |
83 | // 将 body 发送给前端...
84 |
85 | }, function (err) {
86 | console.error(err);
87 | });
88 | ```
89 |
90 | ## `validate(result, callback)`
91 |
92 | ```js
93 | // 回调形式
94 | captcha.validate({
95 | challenge: 'xxx',
96 | validate: 'xxx',
97 | seccode: 'xxx'
98 | }, function (err, success) {
99 |
100 | if (err) {
101 | console.error(err);
102 | return;
103 | }
104 |
105 | if (success) {
106 |
107 | // 二次验证成功,运行用户的操作
108 |
109 | } else {
110 |
111 | // 二次验证失败,不允许用户的操作
112 |
113 | }
114 |
115 | });
116 |
117 | // Promise 形式
118 | captcha.validate({
119 | challenge: 'xxx',
120 | validate: 'xxx',
121 | seccode: 'xxx'
122 | }).then(function (success) {
123 |
124 | if (success) {
125 |
126 | // 二次验证成功,运行用户的操作
127 |
128 | } else {
129 |
130 | // 二次验证失败,不允许用户的操作
131 |
132 | }
133 | }, function (err) {
134 | console.error(err);
135 | })
136 | ```
137 |
138 | ### 使用前,强烈建议您阅读我们的[入门文档](http://www.geetest.com/install/sections/idx-main-frame.html)
139 |
140 | ### 更新历史:[CHANGELOG](CHANGELOG.md)
141 |
142 |
--------------------------------------------------------------------------------
/demo/app.js:
--------------------------------------------------------------------------------
1 | var express = require("express");
2 | var bodyParser = require("body-parser");
3 |
4 | var Geetest = require('../gt-sdk');
5 |
6 | var app = express();
7 | app.use(bodyParser.json());
8 | app.use(bodyParser.urlencoded({extended: true}));
9 | app.use(express.static('./'));
10 | app.get("/", function (req, res) {
11 | res.sendFile(__dirname + "/login.html");
12 | });
13 |
14 | // pc 端接口
15 |
16 | var pcGeetest = new Geetest({
17 | geetest_id: 'b46d1900d0a894591916ea94ea91bd2c',
18 | geetest_key: '36fc3fe98530eea08dfc6ce76e3d24c4'
19 | });
20 | app.get("/pc-geetest/register", function (req, res) {
21 |
22 | // 向极验申请每次验证所需的challenge
23 | pcGeetest.register(function (err, data) {
24 | if (err) {
25 | console.error(err);
26 | return;
27 | }
28 |
29 | if (!data.success) {
30 | // 进入 failback,如果一直进入此模式,请检查服务器到极验服务器是否可访问
31 | // 可以通过修改 hosts 把极验服务器 api.geetest.com 指到不可访问的地址
32 |
33 | // 为以防万一,你可以选择以下两种方式之一:
34 |
35 | // 1. 继续使用极验提供的failback备用方案
36 | res.send(data);
37 |
38 | // 2. 使用自己提供的备用方案
39 | // todo
40 |
41 | } else {
42 | // 正常模式
43 | res.send(data);
44 | }
45 | });
46 | });
47 | app.post("/pc-geetest/validate", function (req, res) {
48 |
49 | // 对ajax提供的验证凭证进行二次验证
50 | pcGeetest.validate({
51 | challenge: req.body.geetest_challenge,
52 | validate: req.body.geetest_validate,
53 | seccode: req.body.geetest_seccode
54 | }, function (err, success) {
55 |
56 | if (err) {
57 | // 网络错误
58 | res.send({
59 | status: "error",
60 | info: err
61 | });
62 | } else if (!success) {
63 |
64 | // 二次验证失败
65 | res.send({
66 | status: "fail",
67 | info: '登录失败'
68 | });
69 | } else {
70 |
71 | res.send({
72 | status: "success",
73 | info: '登录成功'
74 | });
75 | }
76 | });
77 | });
78 | app.post("/pc-geetest/form-validate", function (req, res) {
79 |
80 | // 对form表单提供的验证凭证进行验证
81 | pcGeetest.validate({
82 |
83 | challenge: req.body.geetest_challenge,
84 | validate: req.body.geetest_validate,
85 | seccode: req.body.geetest_seccode
86 |
87 | }, function (err, success) {
88 |
89 | if (err) {
90 | // 网络错误
91 | res.send(err);
92 |
93 | } else if (!success) {
94 |
95 | // 二次验证失败
96 | res.send("
登录失败
");
97 |
98 | } else {
99 | res.send("登录成功
");
100 | }
101 |
102 | });
103 | });
104 |
105 | // 移动端接口
106 | var mobileGeetest = new Geetest({
107 | geetest_id: '7c25da6fe21944cfe507d2f9876775a9',
108 | geetest_key: 'f5883f4ee3bd4fa8caec67941de1b903'
109 | });
110 | app.get("/mobile-geetest/register", function (req, res) {
111 |
112 | // 向极验申请每次验证所需的challenge
113 | mobileGeetest.register().then(function (data) {
114 |
115 | if (!data.success) {
116 | // 进入 failback,如果一直进入此模式,请检查服务器到极验服务器是否可访问
117 | // 可以通过修改 hosts 把极验服务器 api.geetest.com 指到不可访问的地址
118 |
119 | // 为以防万一,你可以选择以下两种方式之一:
120 |
121 | // 1. 继续使用极验提供的failback备用方案
122 | res.send(data);
123 |
124 | // 2. 使用自己提供的备用方案
125 | // todo
126 |
127 | } else {
128 | // 正常模式
129 | res.send(data);
130 | }
131 |
132 | }, function (err) {
133 |
134 | console.error(err);
135 |
136 | });
137 | });
138 | app.post("/mobile-geetest/validate", function (req, res) {
139 |
140 | // 对ajax提供的验证凭证进行二次验证
141 | mobileGeetest.validate({
142 | challenge: req.body.geetest_challenge,
143 | validate: req.body.geetest_validate,
144 | seccode: req.body.geetest_seccode
145 | }).then(function (success) {
146 | if (success) {
147 | res.send({
148 | status: "success",
149 | info: '登录成功'
150 | });
151 | } else {
152 | res.send({
153 | status: "fail",
154 | info: '登录失败'
155 | });
156 | }
157 | }, function (err) {
158 | // 网络错误
159 | res.send({
160 | status: "error",
161 | info: err
162 | });
163 | });
164 | });
165 |
166 | var port = 9988;
167 | app.listen(port, function () {
168 | console.log('listening at http://localhost:' + port)
169 | });
170 |
--------------------------------------------------------------------------------
/demo/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 |
--------------------------------------------------------------------------------
/demo/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | gt-node-sdk-demo
7 |
74 |
75 |
76 | 极验验证SDKDemo
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
105 |
106 |
161 |
162 |
163 |
164 |
184 |
185 |
223 |
224 |
225 |
226 |
243 |
244 |
301 |
302 |
303 |
--------------------------------------------------------------------------------
/gt-sdk.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var crypto = require('crypto'),
4 | request = require('request'),
5 | pkg = require("./package.json");
6 |
7 | var md5 = function (str) {
8 | return crypto.createHash('md5').update(String(str)).digest('hex');
9 | };
10 |
11 | var randint = function (from, to) {
12 | // range: from ~ to
13 | return Math.floor(Math.random() * (to - from + 1) + from);
14 | };
15 |
16 | function Geetest(config) {
17 | if (!config.geetest_id) {
18 | throw new Error('Geetest ID Required');
19 | }
20 | if (!config.geetest_key) {
21 | throw new Error("Geetest KEY Required");
22 | }
23 | if (config.protocol) {
24 | this.PROTOCOL = config.protocol;
25 | }
26 | if (config.api_server) {
27 | this.API_SERVER = config.api_server;
28 | }
29 |
30 | this.geetest_id = config.geetest_id;
31 | this.geetest_key = config.geetest_key;
32 | }
33 |
34 | Geetest.prototype = {
35 |
36 | PROTOCOL: 'http://',
37 | API_SERVER: 'api.geetest.com',
38 | VALIDATE_PATH: '/validate.php',
39 | REGISTER_PATH: '/register.php',
40 |
41 | validate: function (result, callback) {
42 | var that = this;
43 |
44 | return new Promise(function (resolve, reject) {
45 |
46 | that._validate(result, function (err, data) {
47 | if (typeof callback === 'function') {
48 | callback(err, data);
49 | }
50 | if (err) {
51 | reject(err);
52 | } else {
53 | resolve(data);
54 | }
55 | });
56 | })
57 | },
58 |
59 | _validate: function (result, callback) {
60 | var challenge = result.challenge;
61 | var validate = result.validate;
62 |
63 | if (validate.split('_').length === 3) {
64 |
65 | var validate_strs = validate.split('_');
66 | var encode_ans = validate_strs[0];
67 | var encode_fbii = validate_strs[1];
68 | var encode_igi = validate_strs[2];
69 |
70 | var decode_ans = this._decode_response(challenge, encode_ans);
71 | var decode_fbii = this._decode_response(challenge, encode_fbii);
72 | var decode_igi = this._decode_response(challenge, encode_igi);
73 |
74 | var validate_result = this._validate_fail_image(decode_ans, decode_fbii, decode_igi);
75 |
76 | if (validate_result === 1) {
77 | callback(null, true);
78 | } else {
79 | callback(null, false);
80 | }
81 |
82 | } else {
83 |
84 | var hash = this.geetest_key + 'geetest' + result.challenge;
85 | if (result.validate === md5(hash)) {
86 | var url = this.PROTOCOL + this.API_SERVER + this.VALIDATE_PATH;
87 | request.post(url, {
88 | form: {
89 | seccode: result.seccode
90 | }
91 | }, function (err, res, body) {
92 | if (err) {
93 | callback(err);
94 | } else {
95 | callback(null, body === md5(result.seccode));
96 | }
97 | });
98 | } else {
99 | callback(null, false);
100 | }
101 | }
102 | },
103 |
104 | _validate_fail_image: function (ans, full_bg_index, img_grp_index) {
105 |
106 | var thread = 3;
107 | var full_bg_name = md5(full_bg_index).slice(0, 10);
108 | var bg_name = md5(img_grp_index).slice(10, 20);
109 | var answer_decode = '';
110 | var i;
111 | for (i = 0; i < 9; i = i + 1) {
112 | if (i % 2 == 0) {
113 | answer_decode += full_bg_name[i];
114 | } else {
115 | answer_decode += bg_name[i];
116 | }
117 | }
118 | var x_decode = answer_decode.slice(4);
119 | var x_int = parseInt(x_decode, 16);
120 | var result = x_int % 200;
121 | if (result < 40) {
122 | result = 40;
123 | }
124 | if (Math.abs(ans - result) < thread) {
125 | return 1;
126 | } else {
127 | return 0;
128 | }
129 | },
130 |
131 | _decode_response: function (challenge, userresponse) {
132 | if (userresponse.length > 100) {
133 | return 0;
134 | }
135 | var shuzi = [1, 2, 5, 10, 50];
136 | var chongfu = [];
137 | var key = {};
138 | var count = 0, i, len;
139 | for (i = 0, len = challenge.length; i < len; i = i + 1) {
140 | var c = challenge[i];
141 | if (chongfu.indexOf(c) === -1) {
142 | chongfu.push(c);
143 | key[c] = shuzi[count % 5];
144 | count += 1;
145 | }
146 | }
147 | var res = 0;
148 | for (i = 0, len = userresponse.length; i < len; i = i + 1) {
149 | res += key[userresponse[i]] || 0;
150 | }
151 | res = res - this._decode_rand_base(challenge);
152 | return res;
153 | },
154 |
155 | _decode_rand_base: function (challenge) {
156 | var str_base = challenge.slice(32);
157 | var i, len, temp_array = [];
158 | for (i = 0, len = str_base.length; i < len; i = i + 1) {
159 | var temp_char = str_base[i];
160 | var temp_ascii = temp_char.charCodeAt(0);
161 | var result = temp_ascii > 57 ? temp_ascii - 87 : temp_ascii - 48;
162 | temp_array.push(result);
163 | }
164 | var decode_res = temp_array[0] * 36 + temp_array[1];
165 | return decode_res;
166 | },
167 |
168 | register: function (callback) {
169 |
170 | var that = this;
171 | return new Promise(function (resolve, reject) {
172 | that._register(function (err, data) {
173 | if (typeof callback === 'function') {
174 | callback(err, data);
175 | }
176 | if (err) {
177 | reject(err);
178 | } else {
179 | resolve(data);
180 | }
181 | });
182 | });
183 | },
184 |
185 | _register: function (callback) {
186 | var url = this.PROTOCOL + this.API_SERVER + this.REGISTER_PATH
187 | + '?gt=' + this.geetest_id + '&sdk=Node_' + pkg.version;
188 |
189 | var that = this;
190 | request.get(url, {timeout: 2000}, function (err, res, challenge) {
191 |
192 | if (err || challenge.length !== 32) {
193 |
194 | // fallback
195 | callback(null, {
196 | success: 0,
197 | challenge: that._make_challenge(),
198 | gt: that.geetest_id
199 | });
200 |
201 | } else {
202 |
203 | callback(null, {
204 | success: 1,
205 | challenge: md5(challenge + that.geetest_key),
206 | gt: that.geetest_id
207 | });
208 | }
209 | });
210 | },
211 |
212 | _make_challenge: function () {
213 | var rnd1 = randint(0, 90);
214 | var rnd2 = randint(0, 90);
215 | var md5_str1 = md5(rnd1);
216 | var md5_str2 = md5(rnd2);
217 |
218 | return md5_str1 + md5_str2.slice(0, 2);
219 | }
220 | };
221 |
222 | module.exports = Geetest;
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "geetest",
3 | "version": "4.1.2",
4 | "description": "Node SDK for Geetest captcha",
5 | "main": "gt-sdk.js",
6 | "keywords": [
7 | "Geetest",
8 | "Captcha"
9 | ],
10 | "contributors": [
11 | {
12 | "name": "zhangcx93",
13 | "email": "zhangcx93@gmail.com",
14 | "url": "http://www.geetest.com"
15 | },
16 | {
17 | "name": "lhtin",
18 | "email": "lehuading@qq.com",
19 | "url": "http://www.geetest.com"
20 | }
21 | ],
22 | "license": "ISC",
23 | "dependencies": {
24 | "request": "^2.54.0"
25 | },
26 | "devDependencies": {
27 | "express": "^4.13.3",
28 | "body-parser": "^1.14.1"
29 | },
30 | "repository": "https://github.com/GeeTeam/gt-node-sdk",
31 | "scripts": {
32 | "start": "node demo/app.js"
33 | }
34 | }
--------------------------------------------------------------------------------