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

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

 <template>
<div class="login">
<h1>滑动模式</h1>
<form id="form">
<div>
<label for="username">用户名:</label>
<input class="inp" id="username" type="text" value="用户名">
</div>
<br>
<div>
<label for="password">密码:</label>
<input class="inp" id="password" type="password" value="123456">
</div>
<br>
<div>
<label>完成验证:</label>
<div id="captcha">
<p id="wait" class="show">正在加载验证码......</p>
</div>
</div>
<br>
<div id="btn" class="btn">提交</div>
</form>
</div>
</template> <script>
import jqu from "../../static/js/jquery-2.1.0.js" ; //在验证插件里面有用到jquery方便对节点的操作
import gt from "../../static/js/gt.js" ;//用于加载id对应的验证码库,并支持宕机模式 * 暴露 initGeetest 进行验证码的初始化
import slid from "../../static/js/slider.js" ; //页面对接口的调用 export default {
name: 'login',
data () {
return {
msg: ''
}
}
}
</script> <!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
body {
margin: 50px 0;
text-align: center;
font-family: "PingFangSC-Regular", "Open Sans", Arial, "Hiragino Sans GB", "Microsoft YaHei", "STHeiti", "WenQuanYi Micro Hei", SimSun, sans-serif;
} .inp {
border: 1px solid #cccccc;
border-radius: 2px;
padding: 0 10px;
width: 278px;
height: 40px;
font-size: 18px;
} .btn {
display: inline-block;
box-sizing: border-box;
border: 1px solid #cccccc;
border-radius: 2px;
width: 100px;
height: 40px;
line-height: 40px;
font-size: 16px;
color: #666;
cursor: pointer;
background: white linear-gradient(180deg, #ffffff 0%, #f3f3f3 100%);
} .btn:hover {
background: white linear-gradient(0deg, #ffffff 0%, #f3f3f3 100%)
} #captcha {
width: 300px;
display: inline-block;
} label {
vertical-align: top;
display: inline-block;
width: 80px;
text-align: right;
} #wait {
text-align: left;
color: #666;
margin: 0;
}
</style>

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

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

  var handler = function (captchaObj) {
captchaObj.appendTo('#captcha');
captchaObj.onReady(function () {
$("#wait").hide();
});
$('#btn').click(function () {
var result = captchaObj.getValidate();
if (!result) {
return alert('请完成验证');
}
$.ajax({
url: 'http://localhost:3000/gt/validate-slide', //这里的地址是根据你的后台接口的地址,我这里是这样的
type: 'POST',
dataType: 'json',
data: {
geetest_challenge: result.geetest_challenge,
geetest_validate: result.geetest_validate,
geetest_seccode: result.geetest_seccode
},
success: function (data) {
if (data.status === 'success') {
alert('登录成功');
} else if (data.status === 'fail') {
alert('登录失败,请完成验证');
captchaObj.reset();
}
}
});
})
// 更多接口说明请参见:http://docs.geetest.com/install/client/web-front/
}; $.ajax({//这个地址也是需要根据自己的后台接口地址来改动
url: "http://localhost:3000/gt/register-slide?t=" + (new Date()).getTime(), // 加随机数防止缓存
type: "get",
dataType: "json",
success: function (data) {
console.log(data)
// 调用 initGeetest 进行初始化
// 参数1:配置参数
// 参数2:回调,回调的第一个参数验证码对象,之后可以使用它调用相应的接口 initGeetest({
// 以下 4 个配置参数为必须,不能缺少
gt: data.gt,
challenge: data.challenge,
offline: !data.success, // 表示用户后台检测极验服务器是否宕机
new_captcha: data.new_captcha, // 用于宕机时表示是新验证码的宕机 product: "float", // 产品形式,包括:float,popup
width: "300px"
// 更多配置参数说明请参见:http://docs.geetest.com/install/client/web-front/
}, handler);
}
});

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

 var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser'); var index = require('./routes/index');
var users = require('./routes/users'); /*这里引入极客验证的包 */
var Geetest = require('gt3-sdk');
var slide = require('./public/javascripts/slide'); var app = express(); //实现跨域
app.all('*',function(req,res,next){
res.header('Access-Control-Allow-Origin','http://localhost:8081');
res.header('Access-Control-Allow-Methods','PUT,GET,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers','X-Requested-With');
res.header('Access-Control-Allow-Headers','Content-Type');
res.header('Access-Control-Allow-Credentials',true);
next();
}) //session,需要引用下面的包
var session = require('express-session');
app.use(session({
secret: 'classweb531234', //设置 session 签名
name: 'classweb',
cookie: {
maxAge: 60 * 1000 * 60 * 24
}, // 储存的时间 24小时
resave: false, // 每次请求都重新设置session
saveUninitialized: true
})); //极客验证
app.get("/gt/register-slide", function (req, res) {
slide.register(null, function (err, data) {
if (err) {
console.error(err);
res.status(500);
res.send(err);
return;
} if (!data.success) {
req.session.fallback = true;
res.send(data);
} else {
req.session.fallback = false;
res.send(data);
}
});
}); // view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade'); // uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public'))); //极客验证的二次验证,这里可以对用户名,和密码验证
//这里用了req.body所以要放在bodyparser中间件申明后的地方
app.post("/gt/validate-slide", function (req, res) {
// 对ajax提供的验证凭证进行二次验证
slide.validate(req.session.fallback, {
geetest_challenge: req.body.geetest_challenge,
geetest_validate: req.body.geetest_validate,
geetest_seccode: req.body.geetest_seccode
}, function (err, success) { if (err) {
// 网络错误
res.send({
status: "error",
info: err
}); } else if (!success) { // 二次验证失败
res.send({
status: "fail",
info: '登录失败'
});
} else { res.send({
status: "success",
info: '登录成功'
});
}
});
}); app.use('/', index);
app.use('/users', users); // catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
}); // error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page
res.status(err.status || 500);
res.render('error');
}); module.exports = app;

  

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

gt-sdk.js代码如下:

 "use strict";
var crypto = require('crypto'),
request = require('request'),
pkg = require("../../package.json"); //这个地方地址根据自己文件位置来找,找到package.json的相对路径 var md5 = function (str) {
return crypto.createHash('md5').update(String(str)).digest('hex');
};
var randint = function (from, to) {
// range: from ~ to
return Math.floor(Math.random() * (to - from + 1) + from);
};
function Geetest(config) {
if (typeof config.geetest_id !== 'string') {
throw new Error('Geetest ID Required');
}
if (typeof config.geetest_key !== 'string') {
throw new Error("Geetest KEY Required");
}
if (typeof config.protocol === 'string') {
this.PROTOCOL = config.protocol;
}
if (typeof config.api_server === 'string') {
this.API_SERVER = config.api_server;
}
if (typeof config.timeout === 'number') {
this.TIMEOUT = config.timeout;
} this.geetest_id = config.geetest_id;
this.geetest_key = config.geetest_key;
}
Geetest.prototype = {
PROTOCOL: 'http://',
API_SERVER: 'api.geetest.com',
VALIDATE_PATH: '/validate.php',
REGISTER_PATH: '/register.php',
TIMEOUT: 2000,
NEW_CAPTCHA: true,
JSON_FORMAT: 1,
register: function (data, callback) {
var that = this;
return new Promise(function (resolve, reject) {
that._register(data, function (err, data) {
if (typeof callback === 'function') {
callback(err, data);
}
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
},
_register: function (data, callback) {
data = data || {};
var that = this;
request({
url: this.PROTOCOL + this.API_SERVER + this.REGISTER_PATH,
method: 'GET',
timeout: this.TIMEOUT,
json: true,
qs: {
gt: this.geetest_id,
json_format: this.JSON_FORMAT,
sdk: 'Node_' + pkg.version,
client_type: data.client_type || 'unknown',
ip_address: data.ip_address || 'unknown'
}
}, function (err, res, data) {
var challenge;
if (err || !data || !data.challenge) {
// fallback
challenge = that._make_challenge();
callback(null, {
success: 0,
challenge: challenge,
gt: that.geetest_id,
new_captcha: that.NEW_CAPTCHA
});
} else {
challenge = md5(data.challenge + that.geetest_key);
callback(null, {
success: 1,
challenge: challenge,
gt: that.geetest_id,
new_captcha: that.NEW_CAPTCHA
});
}
});
},
validate: function (fallback, result, callback) {
var that = this;
return new Promise(function (resolve, reject) {
that._validate(fallback, result, function (err, data) {
if (typeof callback === 'function') {
callback(err, data);
}
if (err) {
reject(err);
} else {
resolve(data);
}
});
})
},
_validate: function (fallback, result, callback) {
var challenge = result.challenge || result.geetest_challenge;
var validate = result.validate || result.geetest_validate;
var seccode = result.seccode || result.geetest_seccode;
if (fallback) {
if (md5(challenge) === validate) {
callback(null, true);
} else {
callback(null, false);
}
} else {
var hash = this.geetest_key + 'geetest' + challenge;
if (validate === md5(hash)) {
request({
url: this.PROTOCOL + this.API_SERVER + this.VALIDATE_PATH,
method: 'POST',
timeout: this.TIMEOUT,
json: true,
form: {
gt: this.geetest_id,
seccode: seccode,
json_format: this.JSON_FORMAT
}
}, function (err, res, data) {
if (err || !data || !data.seccode) {
callback(err);
} else {
callback(null, data.seccode === md5(seccode));
}
});
} else {
callback(null, false);
}
}
},
_make_challenge: function () {
var rnd1 = randint(0, 90);
var rnd2 = randint(0, 90);
var md5_str1 = md5(rnd1);
var md5_str2 = md5(rnd2);
return md5_str1 + md5_str2.slice(0, 2);
}
}; module.exports = Geetest;

slide.js代码如下:

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

7.效果图如下面:

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

  1. 在前后端分离Web项目中,RBAC实现的研究

    最近手头公司的网站项目终于渐渐走出混沌,走上正轨,任务也轻松了一些,终于有时间整理和总结一下之前做的东西. 以往的项目一般使用模板引擎(如ejs)渲染出完整页面,再发送到浏览器展现.但这次项目的处理方 ...

  2. 前后端分离Web项目中,RBAC实现的研究

    在前后端分离Web项目中,RBAC实现的研究   最近手头公司的网站项目终于渐渐走出混沌,走上正轨,任务也轻松了一些,终于有时间整理和总结一下之前做的东西. 以往的项目一般使用模板引擎(如ejs)渲染 ...

  3. angular+ionic前后端分离开发项目中的使用

    Ionic基于AngularJS构建而成,所以学习一些AngularJS的知识很有必要.Ionic并没有独立开发一套完整的Web应用框架,而是对AngularJS进行了扩展,给它添加了大量界面组件和其 ...

  4. SpringBoot+Vue前后端分离项目,maven package自动打包整合

    起因:看过Dubbo管控台的都知道,人家是个前后端分离的项目,可是一条打包命令能让两个项目整合在一起,我早想这样玩玩了. 1. 建立个maven父项目 next 这个作为父工程,next Finish ...

  5. 解决Django+Vue前后端分离的跨域问题及关闭csrf验证

      前后端分离难免要接触到跨域问题,跨域的相关知识请参:跨域问题,解决之道   在Django和Vue前后端分离的时候也会遇到跨域的问题,因为刚刚接触Django还不太了解,今天花了好长的时间,查阅了 ...

  6. Flask + vue 前后端分离的 二手书App

    一个Flask + vue 前后端分离的 二手书App 效果展示: https://blog.csdn.net/qq_42239520/article/details/88534955 所用技术清单 ...

  7. 喜大普奔,两个开源的 Spring Boot + Vue 前后端分离项目可以在线体验了

    折腾了一周的域名备案昨天终于搞定了. 松哥第一时间想到赶紧把微人事和 V 部落部署上去,我知道很多小伙伴已经等不及了. 1. 也曾经上过线 其实这两个项目当时刚做好的时候,我就把它们部署到服务器上了, ...

  8. 两个开源的 Spring Boot + Vue 前后端分离项目

    折腾了一周的域名备案昨天终于搞定了. 松哥第一时间想到赶紧把微人事和 V 部落部署上去,我知道很多小伙伴已经等不及了. 1. 也曾经上过线 其实这两个项目当时刚做好的时候,我就把它们部署到服务器上了, ...

  9. beego-vue URL重定向(beego和vue前后端分离开发,beego承载vue前端分离页面部署)

    具体过程就不说,是搞这个的自然会动,只把关键代码贴出来. beego和vue前后端分离开发,beego承载vue前端分离页面部署 // landv.cnblogs.com //没有授权转载我的内容,再 ...

随机推荐

  1. nodejs加密Crypto简单例子

    加密技术通常分为两大类:“对称式”和“非对称式”. 对称式加密: 就是加密和解密使用同一个密钥,通常称之为“Session Key ”这种加密技术在当今被广泛采用,如美国政府所采用的DES加密标准就是 ...

  2. 吐槽net下没有靠谱的FastDFS的sdk之使用thrift实现JAVA和C#互通

    事情是这样的,在一个新项目中引入了fastdfs,用这玩意做一些小数据的存储还是很方便的,然后在nuget上就找一个对接FastDFS的sdk,如下图: 一眼就看到了这个top1的sdk,应该会比较靠 ...

  3. 【Python】 编码,en/decode函数以及print语句的一些探索

    昨天晚上在整理hashlib和hmac模块的时候,又看到了编码这块的内容.越看越觉得之前的理解不对,然后想研究一下自己想出来,但是越陷越深..总之把昨晚+今天一个上午的这些自己想到的东西写下来 ● 几 ...

  4. Django+xadmin打造在线教育平台(七)

    十.授课教师 10.1.讲师列表页 拷贝teacher-list.html和teacher-detail.html到templates目录下 先改teacher-list.html,同样继承base. ...

  5. 使用Java编译思想

    1.Java常见的注释有哪些,语法是怎样的? 1)单行注释用//表示,编译器看到//会忽略该行//后的所文本 2)多行注释/* */表示,编译器看到/*时会搜索接下来的*/,忽略掉/* */之间的文本 ...

  6. Beta敏捷冲刺每日报告——Day3

    1.情况简述 Beta阶段Scrum Meeting 敏捷开发起止时间 2017.11.4 00:00 -- 2017.11.5 00:00 讨论时间地点 2017.11.4 晚9:30,电话会议会议 ...

  7. 2017-2018-1 1623 bug终结者 冲刺004

    bug终结者 冲刺004 by 20162322 朱娅霖 整体连接 简要说明 目前,我们已经完成了欢迎界面,主菜单界面,排行榜界面,选项界面,胜利界面,地板类.小人类.墙体类.箱子类和虚拟按键类. 主 ...

  8. ebtables和iptables与linux bridge的交互

    本文为翻译文,不一定是逐字逐句的翻译,而且中间会加上自己的一点见解,如有理解错误的地方,还请大家指出,我定虚心学习.原文见链接 其中斜体字是自己的理解,建议和ebtables手册和iptables手册 ...

  9. 不高兴的小名 nyoj

    不高兴的小明 时间限制:3000 ms  |  内存限制:65535 KB 难度:1   描述    小明又出问题了.妈妈认为聪明的小明应该更加用功学习而变的更加厉害,所以小明除了上学之外,还要参加妈 ...

  10. CSS你所不知的伪元素的用法

    你所不知的 CSS ::before 和 ::after 伪元素用法 博客分类: Div / Css / XML / HTML5   CSS 有两个说不上常用的伪类 :before 和 :after, ...