如何在node和vue前后端分离的项目中使用极客验证,用node的方式

1.用express的脚手架和vue-cli的脚手架搭建的项目目录如下图

2.在vue-client的src/component新建一个login.vue文件,作为登录页面,代码如下

 1 <template>
 2   <div class="login">
 3     <h1>滑动模式</h1>
 4     <form id="form">
 5         <div>
 6             <label for="username">用户名:</label>
 7             <input class="inp" id="username" type="text" value="用户名">
 8         </div>
 9         <br>
10         <div>
11             <label for="password">密码:</label>
12             <input class="inp" id="password" type="password" value="123456">
13         </div>
14         <br>
15         <div>
16             <label>完成验证:</label>
17             <div id="captcha">
18                 <p id="wait" class="show">正在加载验证码......</p>
19             </div>
20         </div>
21         <br>
22         <div id="btn" class="btn">提交</div>
23     </form>
24   </div>
25 </template>
26 
27 <script>
28 import jqu from "../../static/js/jquery-2.1.0.js" ; //在验证插件里面有用到jquery方便对节点的操作
29 import gt from "../../static/js/gt.js" ;//用于加载id对应的验证码库,并支持宕机模式 * 暴露 initGeetest 进行验证码的初始化
30 import slid from "../../static/js/slider.js" ; //页面对接口的调用
31 
32 
33 
34 export default {
35   name: 'login',
36   data () {
37     return {
38       msg: ''
39     }
40   }
41 }
42 </script>
43 
44 <!-- Add "scoped" attribute to limit CSS to this component only -->
45 <style scoped>
46   body {
47             margin: 50px 0;
48             text-align: center;
49             font-family: "PingFangSC-Regular", "Open Sans", Arial, "Hiragino Sans GB", "Microsoft YaHei", "STHeiti", "WenQuanYi Micro Hei", SimSun, sans-serif;
50         }
51 
52         .inp {
53             border: 1px solid #cccccc;
54             border-radius: 2px;
55             padding: 0 10px;
56             width: 278px;
57             height: 40px;
58             font-size: 18px;
59         }
60 
61         .btn {
62             display: inline-block;
63             box-sizing: border-box;
64             border: 1px solid #cccccc;
65             border-radius: 2px;
66             width: 100px;
67             height: 40px;
68             line-height: 40px;
69             font-size: 16px;
70             color: #666;
71             cursor: pointer;
72             background: white linear-gradient(180deg, #ffffff 0%, #f3f3f3 100%);
73         }
74 
75         .btn:hover {
76             background: white linear-gradient(0deg, #ffffff 0%, #f3f3f3 100%)
77         }
78 
79         #captcha {
80             width: 300px;
81             display: inline-block;
82         }
83 
84         label {
85             vertical-align: top;
86             display: inline-block;
87             width: 80px;
88             text-align: right;
89         }
90 
91         #wait {
92             text-align: left;
93             color: #666;
94             margin: 0;
95         }
96 </style>
View Code

 

3.这里是gt.js,放在static/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 });
View Code

 

4.下面是slider.js,也是放在static/js文件夹下面(使用中只用改动两个接口的地址,和自己写的后台地址对应上)

 

 1  var handler = function (captchaObj) {
 2         captchaObj.appendTo('#captcha');
 3         captchaObj.onReady(function () {
 4             $("#wait").hide();
 5         });
 6         $('#btn').click(function () {
 7             var result = captchaObj.getValidate();
 8             if (!result) {
 9                 return alert('请完成验证');
10             }
11             $.ajax({
12                 url: 'http://localhost:3000/gt/validate-slide', //这里的地址是根据你的后台接口的地址,我这里是这样的
13                 type: 'POST',
14                 dataType: 'json',
15                 data: {
16                     geetest_challenge: result.geetest_challenge,
17                     geetest_validate: result.geetest_validate,
18                     geetest_seccode: result.geetest_seccode
19                 },
20                 success: function (data) {
21                     if (data.status === 'success') {
22                         alert('登录成功');
23                     } else if (data.status === 'fail') {
24                         alert('登录失败,请完成验证');
25                         captchaObj.reset();
26                     }
27                 }
28             });
29         })
30         // 更多接口说明请参见:http://docs.geetest.com/install/client/web-front/
31     };
32 
33 
34     $.ajax({//这个地址也是需要根据自己的后台接口地址来改动
35         url: "http://localhost:3000/gt/register-slide?t=" + (new Date()).getTime(), // 加随机数防止缓存
36         type: "get",
37         dataType: "json",
38         success: function (data) {
39                 console.log(data)
40             // 调用 initGeetest 进行初始化
41             // 参数1:配置参数
42             // 参数2:回调,回调的第一个参数验证码对象,之后可以使用它调用相应的接口
43 
44             initGeetest({
45                 // 以下 4 个配置参数为必须,不能缺少
46                 gt: data.gt,
47                 challenge: data.challenge,
48                 offline: !data.success, // 表示用户后台检测极验服务器是否宕机
49                 new_captcha: data.new_captcha, // 用于宕机时表示是新验证码的宕机
50 
51                 product: "float", // 产品形式,包括:float,popup
52                 width: "300px"
53                 // 更多配置参数说明请参见:http://docs.geetest.com/install/client/web-front/
54             }, handler);
55         }
56     });
View Code

 

5.后台的配置,在serviceget文件夹的主入口文件app.js中插入验证的接口处理;

  1 var express = require('express');
  2 var path = require('path');
  3 var favicon = require('serve-favicon');
  4 var logger = require('morgan');
  5 var cookieParser = require('cookie-parser');
  6 var bodyParser = require('body-parser');
  7 
  8 var index = require('./routes/index');
  9 var users = require('./routes/users');
 10 
 11 
 12 /*这里引入极客验证的包 */
 13 var Geetest = require('gt3-sdk');
 14 var slide = require('./public/javascripts/slide');
 15 
 16 var app = express();
 17 
 18 
 19 //实现跨域
 20 app.all('*',function(req,res,next){
 21     res.header('Access-Control-Allow-Origin','http://localhost:8081');
 22     res.header('Access-Control-Allow-Methods','PUT,GET,POST,DELETE,OPTIONS');
 23     res.header('Access-Control-Allow-Headers','X-Requested-With');
 24     res.header('Access-Control-Allow-Headers','Content-Type');
 25     res.header('Access-Control-Allow-Credentials',true);
 26     next();
 27 })
 28 
 29 //session,需要引用下面的包
 30 var session = require('express-session');
 31 app.use(session({
 32     secret: 'classweb531234', //设置 session 签名
 33     name: 'classweb',
 34     cookie: {
 35         maxAge: 60 * 1000 * 60 * 24
 36     }, // 储存的时间 24小时
 37     resave: false, // 每次请求都重新设置session
 38     saveUninitialized: true
 39 }));
 40 
 41 
 42 
 43 //极客验证
 44 app.get("/gt/register-slide", function (req, res) {
 45   slide.register(null, function (err, data) {
 46       if (err) {
 47           console.error(err);
 48           res.status(500);
 49           res.send(err);
 50           return;
 51       }
 52 
 53       if (!data.success) {
 54           req.session.fallback = true;
 55           res.send(data);
 56       } else {
 57           req.session.fallback = false;
 58           res.send(data);
 59       }
 60   });
 61 });
 62 
 63 
 64 
 65 
 66 // view engine setup
 67 app.set('views', path.join(__dirname, 'views'));
 68 app.set('view engine', 'jade');
 69 
 70 // uncomment after placing your favicon in /public
 71 //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
 72 app.use(logger('dev'));
 73 app.use(bodyParser.json());
 74 app.use(bodyParser.urlencoded({ extended: false }));
 75 app.use(cookieParser());
 76 app.use(express.static(path.join(__dirname, 'public')));
 77 
 78 //极客验证的二次验证,这里可以对用户名,和密码验证
 79 //这里用了req.body所以要放在bodyparser中间件申明后的地方
 80 app.post("/gt/validate-slide", function (req, res) {
 81   // 对ajax提供的验证凭证进行二次验证
 82   slide.validate(req.session.fallback, {
 83       geetest_challenge: req.body.geetest_challenge,
 84       geetest_validate: req.body.geetest_validate,
 85       geetest_seccode: req.body.geetest_seccode
 86   }, function (err, success) {
 87 
 88       if (err) {
 89           // 网络错误
 90           res.send({
 91               status: "error",
 92               info: err
 93           });
 94 
 95       } else if (!success) {
 96 
 97           // 二次验证失败
 98           res.send({
 99               status: "fail",
100               info: '登录失败'
101           });
102       } else {
103 
104           res.send({
105               status: "success",
106               info: '登录成功'
107           });
108       }
109   });
110 });
111 
112 
113 
114 
115 
116 
117 app.use('/', index);
118 app.use('/users', users);
119 
120 // catch 404 and forward to error handler
121 app.use(function(req, res, next) {
122   var err = new Error('Not Found');
123   err.status = 404;
124   next(err);
125 });
126 
127 // error handler
128 app.use(function(err, req, res, next) {
129   // set locals, only providing error in development
130   res.locals.message = err.message;
131   res.locals.error = req.app.get('env') === 'development' ? err : {};
132 
133   // render the error page
134   res.status(err.status || 500);
135   res.render('error');
136 });
137 
138 module.exports = app;
View Code

  

6.在后台serviceget/public/javascripts 文件夹下面复制进gt-sdk.js 和slide.js(gt-sdk.js是对sdk包的处理,slide.js是保存的自己的密钥,和对gt-sdk.js方法的引用)

gt-sdk.js代码如下:

  1 "use strict";
  2 var crypto = require('crypto'),
  3     request = require('request'),
  4     pkg = require("../../package.json");  //这个地方地址根据自己文件位置来找,找到package.json的相对路径
  5 
  6 var md5 = function (str) {
  7     return crypto.createHash('md5').update(String(str)).digest('hex');
  8 };
  9 var randint = function (from, to) {
 10     // range: from ~ to
 11     return Math.floor(Math.random() * (to - from + 1) + from);
 12 };
 13 function Geetest(config) {
 14     if (typeof config.geetest_id !== 'string') {
 15         throw new Error('Geetest ID Required');
 16     }
 17     if (typeof config.geetest_key !== 'string') {
 18         throw new Error("Geetest KEY Required");
 19     }
 20     if (typeof config.protocol === 'string') {
 21         this.PROTOCOL = config.protocol;
 22     }
 23     if (typeof config.api_server === 'string') {
 24         this.API_SERVER = config.api_server;
 25     }
 26     if (typeof config.timeout === 'number') {
 27         this.TIMEOUT = config.timeout;
 28     }
 29 
 30     this.geetest_id = config.geetest_id;
 31     this.geetest_key = config.geetest_key;
 32 }
 33 Geetest.prototype = {
 34     PROTOCOL: 'http://',
 35     API_SERVER: 'api.geetest.com',
 36     VALIDATE_PATH: '/validate.php',
 37     REGISTER_PATH: '/register.php',
 38     TIMEOUT: 2000,
 39     NEW_CAPTCHA: true,
 40     JSON_FORMAT: 1,
 41     register: function (data, callback) {
 42         var that = this;
 43         return new Promise(function (resolve, reject) {
 44             that._register(data, function (err, data) {
 45                 if (typeof callback === 'function') {
 46                     callback(err, data);
 47                 }
 48                 if (err) {
 49                     reject(err);
 50                 } else {
 51                     resolve(data);
 52                 }
 53             });
 54         });
 55     },
 56     _register: function (data, callback) {
 57         data = data || {};
 58         var that = this;
 59         request({
 60             url: this.PROTOCOL + this.API_SERVER + this.REGISTER_PATH,
 61             method: 'GET',
 62             timeout: this.TIMEOUT,
 63             json: true,
 64             qs: {
 65                 gt: this.geetest_id,
 66                 json_format: this.JSON_FORMAT,
 67                 sdk: 'Node_' + pkg.version,
 68                 client_type: data.client_type || 'unknown',
 69                 ip_address: data.ip_address || 'unknown'
 70             }
 71         }, function (err, res, data) {
 72             var challenge;
 73             if (err || !data || !data.challenge) {
 74                 // fallback
 75                 challenge = that._make_challenge();
 76                 callback(null, {
 77                     success: 0,
 78                     challenge: challenge,
 79                     gt: that.geetest_id,
 80                     new_captcha: that.NEW_CAPTCHA
 81                 });
 82             } else {
 83                 challenge = md5(data.challenge + that.geetest_key);
 84                 callback(null, {
 85                     success: 1,
 86                     challenge: challenge,
 87                     gt: that.geetest_id,
 88                     new_captcha: that.NEW_CAPTCHA
 89                 });
 90             }
 91         });
 92     },
 93     validate: function (fallback, result, callback) {
 94         var that = this;
 95         return new Promise(function (resolve, reject) {
 96             that._validate(fallback, result, function (err, data) {
 97                 if (typeof callback === 'function') {
 98                     callback(err, data);
 99                 }
100                 if (err) {
101                     reject(err);
102                 } else {
103                     resolve(data);
104                 }
105             });
106         })
107     },
108     _validate: function (fallback, result, callback) {
109         var challenge = result.challenge || result.geetest_challenge;
110         var validate = result.validate || result.geetest_validate;
111         var seccode = result.seccode || result.geetest_seccode;
112         if (fallback) {
113             if (md5(challenge) === validate) {
114                 callback(null, true);
115             } else {
116                 callback(null, false);
117             }
118         } else {
119             var hash = this.geetest_key + 'geetest' + challenge;
120             if (validate === md5(hash)) {
121                 request({
122                     url: this.PROTOCOL + this.API_SERVER + this.VALIDATE_PATH,
123                     method: 'POST',
124                     timeout: this.TIMEOUT,
125                     json: true,
126                     form: {
127                         gt: this.geetest_id,
128                         seccode: seccode,
129                         json_format: this.JSON_FORMAT
130                     }
131                 }, function (err, res, data) {
132                     if (err || !data || !data.seccode) {
133                         callback(err);
134                     } else {
135                         callback(null, data.seccode === md5(seccode));
136                     }
137                 });
138             } else {
139                 callback(null, false);
140             }
141         }
142     },
143     _make_challenge: function () {
144         var rnd1 = randint(0, 90);
145         var rnd2 = randint(0, 90);
146         var md5_str1 = md5(rnd1);
147         var md5_str2 = md5(rnd2);
148         return md5_str1 + md5_str2.slice(0, 2);
149     }
150 };
151 
152 module.exports = Geetest;
View Code

 

slide.js代码如下:

var Geetest = require('./gt-sdk');
var captcha = new Geetest({
    geetest_id: '***************************', //注册极客获得的id
    geetest_key: '**************************'  //注册极客获得的key
});

module.exports = captcha;

7.效果图如下面:

posted @ 2018-04-13 10:32  missLiuliu  阅读(2026)  评论(0编辑  收藏  举报