geetest官方文档地址:https://docs.geetest.com/css
产品——极速验证:基于深度学习的人机识别应用。极验「行为验证」是一项能够帮助你的网站与APP识别与拦截机器程序批量自动化操做的SaaS应用。它是由极验开发的新一代人机验证产品,它不基于传统“问题-答案”的检测模式,而是经过利用深度学习对验证过程当中产生的行为数据进行高维分析,发现人机行为模式与行为特征的差别,更加精准地区分人机行为。前端
客户端官方文档:https://docs.geetest.com/install/deploy/client/web/vue
经过引入 gt.js 文件,引入 initGeetest 初始化函数。ios
<script src="gt.js"></script>
注意:行为验证要求初始化在业务页面加载时同时初始化,不然验证没法读取用户在业务页面操做的行为数据,致使验证策略失效。web
这里的 gt.js 文件,它用于加载对应的验证JS库。在每一个后端语言的sdk中都存有一份,开发者部署到实际环境须要将该文件复制到相应的项目中使用。ajax
以前该文件地址是 https://static.geetest.com/static/tools/gt.js ,改成存放在用户的项目中,防止静态服务器出问题没法加载该文件。vuex
使用初始化函数 initGeetest
初始化后,回调的第一个参数便是验证明例,它的第二个参数是一个回调,以下代码所示。vue-cli
ajax({ url: "API1接口(详见服务端部署)", type: "get", dataType: "json", success: function (data) { //请检测data的数据结构, 保证data.gt, data.challenge, data.success有值 initGeetest({ // 如下配置参数来自服务端 SDK gt: data.gt, challenge: data.challenge, offline: !data.success, new_captcha: true }, function (captchaObj) { // 这里能够调用验证明例 captchaObj 的实例方法 }) } })
注: 对于同一个页面存在多个验证码场景的初始化,须要每一个验证码场景调用 initGeetest 方法单独进行初始化;若是一个场景下有多个验证入口,须要进行屡次初始化。npm
在行为验证中,绝大多数真实用户仅需点击一下便可经过验证。可是考虑到实际风险状况,被行为验证断定为有风险的请求,会进入下个阶段的验证。此时,行为验证提供了弹出二级验证的交互样式,方便用户兼容本身自己的界面。这里以float浮动式验证为例:编程
initGeetest({ // 省略必须的配置参数 product: 'float' }, function (captchaObj) { captchaObj.appendTo("#captchaBox"); //将验证按钮插入到宿主页面中captchaBox元素内 captchaObj.onReady(function(){ //your code }).onSuccess(function(){ //your code }).onError(function(){ //your code }) });
观察各大网站使用了geetest的login页面接口信息,能够发现请求发回的数据中包含gt、challenge、success的值。在单页面应用Login.vue中实现geetest验证。
在前端项目中建立/src/global/gt.js文件,写入文件地址 https://static.geetest.com/static/tools/gt.js 的内容。
在/src/main.js中全局引入gt.js文件:
import '../static/global/gt.js'
在 /src/restful/api.js 中添加geetest接口以下所示:
// geetest接口 export const geetest = ()=>{ return Axios.get('captcha_check/').then(res=>res.data); }
在getGeetest方法中调用geetest初始化函数初始化:
<script> export default { name: 'Login', data(){ return { username: "", password: "" } }, methods:{ getGeetest(){ this.$http.geetest() .then(res=>{ console.log(res); let data = res.data; //请检测data的数据结构, 保证data.gt, data.challenge, data.success有值 initGeetest({ // 如下配置参数来自服务端 SDK gt: data.gt, // 验证id,极验后台申请获得 challenge: data.challenge, // 验证流水号,后服务端SDK向极验服务器申请获得 offline: !data.success, // 极验API服务器是否宕机(即处于fallback状态) new_captcha: true, // 宕机状况下使用,表示验证是3.0仍是2.0,3.0的sdk该字段为true product: popup, // 弹出式展示 with: '100%' // 默认宽度300px }, function (captchaObj) { // 这里能够调用验证明例 captchaObj 的实例方法 captchaObj.appendTo("#geetest"); //将验证按钮插入到宿主页面中captchaObj元素内 captchaObj.onReady(function(){ //your code }).onSuccess(function(){ //your code }).onError(function(){ //your code }) }) }) .catch(err=>{ console.log(err); }) } }, created() { this.getGeetest(); } }; </script>
appendTo
方法用于将验证按钮插到宿主页面,使其显示在页面上。接受的参数能够是 id 选择器(例如 #captcha-box
),或者 DOM 元素对象。
<div id="captcha-box"></div> // 方式一:传入id选择器 <script> initGeetest({ // 省略配置参数 }, function (captchaObj) { captchaObj.appendTo('#captcha-box'); // 省略其余方法的调用 }); </script> // 方式二:传入DOM元素 <script> var captchaBox = document.getElementById('#captcha-box'); initGeetest({ // 省略配置参数 }, function (captchaObj) { captchaObj.appendTo(captchaBox); // 省略其余方法的调用 }); </script>
监听验证按钮的 DOM 生成完毕事件。参数 callback
为函数类型。
<div id="captcha-box"> <div id="loading-tip">加载中,请稍后...</div> </div> <script> initGeetest({ // 省略配置参数 }, function (captchaObj) { captchaObj.appendto('#captcha-box'); // 省略其余方法的调用 captchaObj.onReady(function () { // DOM 准备好后,隐藏 #loading-tip 元素 // 仅做示例用,用您适合的方式隐藏便可 document.getElementById('loading-tip').style.display = 'none'; }); }); </script>
显示效果以下所示:
获取用户进行成功验证(onSuccess
)所获得的结果,该结果用于进行服务端 SDK 进行二次验证。getValidate
方法返回一个对象,该对象包含 geetest_challenge
,geetest_validate
,geetest_seccode
字段。
export default { name: 'Login', data(){ return { username: "", password: "", validateResult: {} // 验证成功后返回的结果,用于服务端sdk二次验证 } }, methods:{ getGeetest(){ this.$http.geetest() .then(res=>{ console.log(res); let data = res.data; // 将当前组件this对象赋值给 _this var _this = this; // 函数的调用方式决定了this的值。this不能在执行期间被赋值,而且在每次函数被调用时this的值也可能会不一样。 //请检测data的数据结构, 保证data.gt, data.challenge, data.success有值 initGeetest({ // 如下配置参数来自服务端 SDK gt: data.gt, // 验证id,极验后台申请获得 challenge: data.challenge, // 验证流水号,后服务端SDK向极验服务器申请获得 offline: !data.success, // 极验API服务器是否宕机(即处于fallback状态) new_captcha: true, // 宕机状况下使用,表示验证是3.0仍是2.0,3.0的sdk该字段为true product: popup, // 弹出式展示 with: '100%' // 默认宽度300px }, function (captchaObj) { // 这里能够调用验证明例 captchaObj 的实例方法 captchaObj.appendTo("#geetest"); //将验证按钮插入到宿主页面中captchaObj元素内 captchaObj.onReady(function(){ //your code }).onSuccess(function(){ //your code console.log(captchaObj); var result = captchaObj.getValidate(); _this.validateResult = result; }).onError(function(){ //your code }) }) }) .catch(err=>{ console.log(err); }) }
在 src/restful/api.js 文件中配置登陆接口:
// 登陆接口 export const userLogin = (params)=>{ // 这个参数至少有5个字段,username,password,geetest_challenge,geetest_validate,geetest_seccode return Axios.post('account/login/', params).then(res=>res.data); };
在登陆按钮上绑定登陆事件:
<button class="login_btn" @click="loginHandler">登陆</button> <p class="go_login" >没有帐号 <span>当即注册</span></p>
添加loginHandler方法:
methods:{ loginHandler(){ let params = { // 5个字段 username: this.username, password: this.password, geet_challenge: this.validateResult.geet_challenge, geet_validate: this.validateResult.geet_validate, geet_seccode: this.validateResult.geet_seccode }; this.$http.userLogin(params) .then(res=>{ console.log(res); }) .catch(err=>{ console.log(err); }) },
在页面登陆查看控制台输出的data信息:
在二次验证成功后,经过编程式导航跳转到Home组件,显示网站首页,可是跳转时须要携带验证时获取的登陆数据信息。
这里使用localstorage来存储登陆信息:
methods:{ loginHandler(){ let params = { // 5个字段 username: this.username, password: this.password, geet_challenge: this.validateResult.geet_challenge, geet_validate: this.validateResult.geet_validate, geet_seccode: this.validateResult.geet_seccode }; this.$http.userLogin(params) .then(res=>{ console.log(res); if (res.error_no === 0){ // 验证成功 this.$router.push({ // 路由跳转到Home组件 name: "Home" }); localStorage.setItem('access_token', res.data.access_token); // token值判断是否登陆 localStorage.setItem('username', res.data.username); // 用户名 localStorage.setItem('avatar', res.data.avatar); // 用户头像 } }) .catch(err=>{ console.log(err); }) },
查看控制台Application中显示的Local Storage信息:
只读的localStorage
属性容许你访问一个Document
源(origin)的对象 Storage
;其存储的数据能在跨浏览器会话保留。localStorage
相似 sessionStorage
,但其区别在于:存储在 localStorage
的数据能够长期保留;而当页面会话结束——也就是说,当页面被关闭时,存储在 sessionStorage
的数据会被清除 。
不管数据存储在 localStorage
仍是 sessionStorage
,它们都特定于页面的协议。
另外,localStorage
中的键值对老是以字符串的形式存储。 (须要注意, 和js对象相比, 键值对老是以字符串的形式存储意味着数值类型会自动转化为字符串类型).
登陆点击后,页面跳转至网站首页(Home组件),同时也使用了localStorage存储了须要保存的对象。可是要想将存储的用户名和头像展现到页首(Header组件)却没法完成。Home组件和Header组件没有关联。须要使用 Vuex 插件,集中式存储管理应该的全部组件的状态,并以相应的规则保证状态以一种可预测的方式变化。
vue-cli构建的项目中不包含vuex,手动引入:
$ npm install vuex -S
安装 Vuex 后,建立 src/store 目录,再建立 src/store/index.js 文件,用做组装模块并导出store。
import Vue from 'vue' import Vuex from 'vuex' //引入vuex // 使用插件 Vue.use(Vuex); //建立vuex中的store对象 let store = new Vuex.Store({ // 三大将 state:{ userInfo: {} }, // 修改state的惟一方法:提交mutations mutations:{ getUserInfo(state, user){ state.userInfo = user; } }, actions:{ getUserInfo({commit}, user){ commit('getUserInfo', user) } } }); export default store; // 抛出store对象
import Vue from 'vue' import Vuex from 'vuex' import App from './App' import router from './router' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' import '../static/global/global.css' import '../static/global/gt.js' Vue.use(ElementUI); Vue.use(Vuex); import * as api from './restful/api' console.log(api); Vue.prototype.$http = api; // store引入 import store from '../src/store/index' Vue.config.productionTip = false; new Vue({ el: '#app', router, store, // store对象挂载到vue实例中 components: { App }, template: '<App/>' });
将store对象挂载到vue实例中后,各个组件中能够经过 this.$store获取当前的store对象。
所以能够经过 this.$store.state.userInfo来获取当前的用户对象。
前面已经在Login组件中写了 loginHandler 方法,来处理登陆事件。
验证成功后将用户信息保存到localStorage中,这里还要将用户信息保存到 store对象的 userInfo 字段中。改写以下所示:
methods:{ loginHandler(){ let params = { // 5个字段 username: this.username, password: this.password, geet_challenge: this.validateResult.geet_challenge, geet_validate: this.validateResult.geet_validate, geet_seccode: this.validateResult.geet_seccode }; this.$http.userLogin(params) .then(res=>{ console.log(res); if (res.error_no === 0){ // 验证成功 this.$router.push({ // 路由跳转到Home组件 name: "Home" }); localStorage.setItem('access_token', res.data.access_token); // token值判断是否登陆 localStorage.setItem('username', res.data.username); // 用户名 localStorage.setItem('avatar', res.data.avatar); // 用户头像 localStorage.setItem('shop_cart_num', res.data.shop_cart_num); // 购物车数量 // dispatch action的行为 this.$store.dispatch('getUserInfo', res.data); } }) .catch(err=>{ console.log(err); }) },
页首组件在用户没有登陆是会显示登陆、注册按钮。在用户注册后应显示用户名及用户头像等信息。
<script> export default { name: 'LuffyHeader', data() { return { headerList: [ {id: '1', name: 'Home', title: '首页'}, {id: '2', name: 'Course', title: '免费课程'}, {id: '3', name: 'LightCourse', title: '轻课'}, {id: '4', name: 'Micro', title: '学位课程'} ] } }, computed: { userInfo(){ return this.$store.state.userInfo; } } }; </script>
methods、watch、computed对比:
判断userInfo.access_token是否有值,没有值显示登陆/注册,有值则显示登陆信息。
<div class="nav-right" v-if="userInfo.access_token"> <span class = 'el-dropdown-link'>学习中心</span> <span class="user">{{userInfo.username}}</span> <img :src="userInfo.avatar" alt=""> <ul class="my_account"> <li>个人帐户<i>></i></li> <li>个人订单<i>></i></li> <li>个人优惠券<i>></i></li> <li>个人消息<span class="msg">({{userInfo.notice_num}})</span><i>></i></li> <li>购物车<span class="count">({{userInfo.shop_cart_num}})</span></li> <li>退出<i>></i></li> </ul> </div> <!-- </el-dropdown> --> <div class="nav-right" v-else> <span>登陆</span> | <span>注册</span> </div>
使用v-show来控制无序列表 <ul> 标签的显示和隐藏。
<template> <!-- 代码略 --> <div class="nav-right" v-if="userInfo.access_token"> <span class = 'el-dropdown-link'>学习中心</span> <span class="user">{{userInfo.username}}</span> <img :src="userInfo.avatar" alt=""> <ul class="my_account" v-show="isShow"> <li>个人帐户<i>></i></li> <li>个人订单<i>></i></li> <li>个人优惠券<i>></i></li> <li>个人消息<span class="msg">({{userInfo.notice_num}})</span><i>></i></li> <li>购物车<span class="count">({{userInfo.shop_cart_num}})</span></li> <li>退出<i>></i></li> </ul> </div> </template> <script> export default { name: 'LuffyHeader', data() { return { headerList: [ // 代码略 ], isShow: false // 默认隐藏 } }, computed: { userInfo(){ return this.$store.state.userInfo; } } }; </script>
isShow默认值为false,即默认隐藏。鼠标移入触发enterHandler事件后ul 显示,鼠标移出触发leaveHandler事件后ul隐藏。
<template> <!--代码略--> <div class="nav-right" v-if="userInfo.access_token" @mouseenter="enterHandler" @mouseleave="leaveHandler"> <span class = 'el-dropdown-link'>学习中心</span> <span class="user">{{userInfo.username}}</span> <img :src="userInfo.avatar" alt=""> <ul class="my_account" v-show="isShow"> <li>个人帐户<i>></i></li> <li>个人订单<i>></i></li> <li>个人优惠券<i>></i></li> <li>个人消息<span class="msg">({{userInfo.notice_num}})</span><i>></i></li> <li>购物车<span class="count">({{userInfo.shop_cart_num}})</span></li> <li>退出<i>></i></li> </ul> </div> <!-- </el-dropdown> --> <div class="nav-right" v-else> <span>登陆</span> | <span>注册</span> </div> <!--代码略--> </template> <script> export default { name: 'LuffyHeader', data() { return { headerList: [ // 代码略 ], isShow: false // 默认隐藏 } }, methods: { enterHandler() { this.isShow = true; }, leaveHandler() { this.isShow = false; } }, computed: { userInfo(){ return this.$store.state.userInfo; } } }; </script>
前面实现了用户登陆,Header显示用户登陆信息。可是若是刷新页面或者跳转到导航栏其余页面。会发现Header显示的不是用户登陆信息,而是 登陆/注册。
所以须要使用Vue-Router 的导航守卫来保存用户始终登陆。
// store引入 import store from '../src/store/index' // 路由全局守卫 router.beforeEach((to, from, next)=>{ console.log(to); console.log(from); next(); // 确保要调用 next 方法,不然钩子就不会被 resolved(发生阻塞) }); Vue.config.productionTip = false;
控制台查看to,from打印信息:
from是从哪里来的路由(当前导航要离开的路由),to是到哪里去的路由(即将要进入的目标路由对象)。
在main.js 中读取localStorage中存储的用户信息。经过 store.dispatch
方法触发Action中的 getUserInfo 方法。
// 路由全局守卫 router.beforeEach((to, from, next)=>{ // console.log(to); // console.log(from); if(localStorage.getItem('access_token')){ // 用户登陆过了 let user = { // 获取用户信息 access_token: localStorage.getItem('access_token'), username: localStorage.getItem('username'), avatar: localStorage.getItem('avatar'), shop_cart_num: localStorage.getItem('shop_cart_num') }; // 经过dispatch调用action中方法getUserInfo store.dispatch('getUserInfo', user); } next(); // 确保要调用 next 方法,不然钩子就不会被 resolved(发生阻塞) });
提交mutation后,实现更改 Vuex 的 store 的 state。
Header组件中 compute 监听到userInfo变化,所以刷新页面或者跳转到导航栏其余页面时,都会正常显示用户登陆信息。