如何在node和vue前后端分离的项目中使用极客验证,用node的方式
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的方式的更多相关文章
- 在前后端分离Web项目中,RBAC实现的研究
最近手头公司的网站项目终于渐渐走出混沌,走上正轨,任务也轻松了一些,终于有时间整理和总结一下之前做的东西. 以往的项目一般使用模板引擎(如ejs)渲染出完整页面,再发送到浏览器展现.但这次项目的处理方 ...
- 前后端分离Web项目中,RBAC实现的研究
在前后端分离Web项目中,RBAC实现的研究 最近手头公司的网站项目终于渐渐走出混沌,走上正轨,任务也轻松了一些,终于有时间整理和总结一下之前做的东西. 以往的项目一般使用模板引擎(如ejs)渲染 ...
- angular+ionic前后端分离开发项目中的使用
Ionic基于AngularJS构建而成,所以学习一些AngularJS的知识很有必要.Ionic并没有独立开发一套完整的Web应用框架,而是对AngularJS进行了扩展,给它添加了大量界面组件和其 ...
- SpringBoot+Vue前后端分离项目,maven package自动打包整合
起因:看过Dubbo管控台的都知道,人家是个前后端分离的项目,可是一条打包命令能让两个项目整合在一起,我早想这样玩玩了. 1. 建立个maven父项目 next 这个作为父工程,next Finish ...
- 解决Django+Vue前后端分离的跨域问题及关闭csrf验证
前后端分离难免要接触到跨域问题,跨域的相关知识请参:跨域问题,解决之道 在Django和Vue前后端分离的时候也会遇到跨域的问题,因为刚刚接触Django还不太了解,今天花了好长的时间,查阅了 ...
- Flask + vue 前后端分离的 二手书App
一个Flask + vue 前后端分离的 二手书App 效果展示: https://blog.csdn.net/qq_42239520/article/details/88534955 所用技术清单 ...
- 喜大普奔,两个开源的 Spring Boot + Vue 前后端分离项目可以在线体验了
折腾了一周的域名备案昨天终于搞定了. 松哥第一时间想到赶紧把微人事和 V 部落部署上去,我知道很多小伙伴已经等不及了. 1. 也曾经上过线 其实这两个项目当时刚做好的时候,我就把它们部署到服务器上了, ...
- 两个开源的 Spring Boot + Vue 前后端分离项目
折腾了一周的域名备案昨天终于搞定了. 松哥第一时间想到赶紧把微人事和 V 部落部署上去,我知道很多小伙伴已经等不及了. 1. 也曾经上过线 其实这两个项目当时刚做好的时候,我就把它们部署到服务器上了, ...
- beego-vue URL重定向(beego和vue前后端分离开发,beego承载vue前端分离页面部署)
具体过程就不说,是搞这个的自然会动,只把关键代码贴出来. beego和vue前后端分离开发,beego承载vue前端分离页面部署 // landv.cnblogs.com //没有授权转载我的内容,再 ...
随机推荐
- Runtime的使用
一.RunTime简介 RunTime简称运行时.OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制. 对于C语言,函数的调用在编译的时候会决定调用哪个函数. 对于OC的函数,属于 ...
- JavaSE中常见的工具类
Arrays 用来操作数组, 常用方法是 sort()和toString()方法 Iterator 我们常说的迭代器就是这哥们,专门用来操作集合元素的工具类 常用方法是: hasNex()t和next ...
- poj-1008-玛雅历
Description 上周末,M.A. Ya教授对古老的玛雅有了一个重大发现.从一个古老的节绳(玛雅人用于记事的工具)中,教授发现玛雅人使用了一个一年有365天的叫做Haab的历法.这个Haab历法 ...
- 【XML】 XML格式一些记录
XML XML格式常用于网络通讯,本身不会有作为而是作为纯文本传输,可以说它是一种独立于应用和硬件的数据传输工具.虽然看起来XML比HTML要更加简单,也知道的更加晚一点,但是需要知道的是,XML才是 ...
- 设计模式 --> (3)策略模式
策略模式 策略模式是指定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换.本模式使得算法可独立于使用它的客户而变化.也就是说这些算法所完成的功能一样,对外的接口一样,只是各自实现上存在差异. ...
- 将 Shiro 作为应用的权限基础 一:shiro的整体架构
将 Shiro 作为应用的权限基础 一:shiro的整体架构 近来在做一个重量级的项目,其中权限.日志.报表.工作量由我负责,工作量还是蛮大的,不过想那么多干嘛,做就是了. 这段时间,接触的东西挺多, ...
- vue-过渡动画
本篇资料参考于官方文档: http://cn.vuejs.org/guide/transitions.html 概述: Vue 在跳转页面时,提供多种不同方式的动画过渡效果. ●in-out:新元素先 ...
- 20155227 实现mypwd
20155227 实现mypwd 1 学习pwd命令 2 研究pwd实现需要的系统调用(man -k; grep),写出伪代码 3 实现mypwd 4 测试mypwd 课堂学习笔记 实现mypwd 在 ...
- 实验四:Android 开发基础
实验四:实验报告 课程:程序设计与数据结构 班级: 1623 姓名: 张旭升 学号:20162329 指导教师:娄嘉鹏 王志强 实验日期:5月26日 实验密级: 非密级 预习程度: 已预习 必修/选修 ...
- DML数据操作语言之查询(二)
当我们查询出了N条记录之后 ,我们知道一共是几条记录,或者这些记录某一字段(列值)的最大值,最小值,平均值等,就可以使用聚合函数. 1.聚合函数 聚合函数会将null 排除在外.但是count(*)例 ...