一、geetest滑动验证

  geetest官方文档地址:https://docs.geetest.com/

  产品——极速验证:基于深度学习的人机识别应用。极验「行为验证」是一项可以帮助你的网站与APP识别与拦截机器程序批量自动化操作的SaaS应用。它是由极验开发的新一代人机验证产品,它不基于传统“问题-答案”的检测模式,而是通过利用深度学习对验证过程中产生的行为数据进行高维分析,发现人机行为模式与行为特征的差异,更加精准地区分人机行为。

1、web部署介绍

  客户端官方文档:https://docs.geetest.com/install/deploy/client/web/

(1)引入初始化函数

  通过引入 gt.js 文件,引入 initGeetest 初始化函数。

<script src="gt.js"></script>

  注意:行为验证要求初始化在业务页面加载时同时初始化,否则验证无法读取用户在业务页面操作的行为数据,导致验证策略失效。

  这里的 gt.js 文件,它用于加载对应的验证JS库。在每个后端语言的sdk中都存有一份,开发者部署到实际环境需要将该文件复制到相应的项目中使用。

  之前该文件地址是 https://static.geetest.com/static/tools/gt.js  ,改为存放在用户的项目中,防止静态服务器出问题无法加载该文件。

(2)调用初始化函数初始化

  使用初始化函数 initGeetest 初始化后,回调的第一个参数即是验证实例,它的第二个参数是一个回调,如下代码所示。

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 方法单独进行初始化;如果一个场景下有多个验证入口,需要进行多次初始化。

(3)product参数设置二级验证

  在行为验证中,绝大多数真实用户仅需点击一下即可通过验证。但是考虑到实际风险情况,被行为验证判定为有风险的请求,会进入下个阶段的验证。此时,行为验证提供了弹出二级验证的交互样式,方便用户兼容自己本身的界面。这里以float浮动式验证为例:

initGeetest({
// 省略必须的配置参数 product: 'float'
}, function (captchaObj) {
captchaObj.appendTo("#captchaBox"); //将验证按钮插入到宿主页面中captchaBox元素内
captchaObj.onReady(function(){
//your code
}).onSuccess(function(){
//your code
}).onError(function(){
//your code
})
});

2、vue项目Login页面geetest实现

  观察各大网站使用了geetest的login页面接口信息,可以发现请求发回的数据中包含gt、challenge、success的值。在单页面应用Login.vue中实现geetest验证。

(1)在项目中全局引入geetest

  在前端项目中创建/src/global/gt.js文件,写入文件地址 https://static.geetest.com/static/tools/gt.js 的内容。

  在/src/main.js中全局引入gt.js文件:

import '../static/global/gt.js'

(2)添加geetest接口

  在 /src/restful/api.js 中添加geetest接口如下所示:

// geetest接口
export const geetest = ()=>{
return Axios.get('captcha_check/').then(res=>res.data);
}

(3)配置getGeetest方法

  在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>

(4)appendTo(position)

  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>

(5)onReady(callback)

  监听验证按钮的 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>

(6)geetest校验显示效果

  显示效果如下所示:

  

二、二次验证数据处理和登录实现

1、getValidate()方法二次校验

  获取用户进行成功验证(onSuccess)所得到的结果,该结果用于进行服务端 SDK 进行二次验证。getValidate 方法返回一个对象,该对象包含 geetest_challengegeetest_validategeetest_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);
})
}

2、配置登录接口

  在 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);
};

3、登录事件

  在登录按钮上绑定登录事件:

<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信息:

  

4、登录数据解析

  在二次验证成功后,通过编程式导航跳转到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对象相比, 键值对总是以字符串的形式存储意味着数值类型会自动转化为字符串类型).

5、用户登录后组件通信问题

  登录点击后,页面跳转至网站首页(Home组件),同时也使用了localStorage存储了需要保存的对象。但是要想将存储的用户名和头像展示到页首(Header组件)却无法完成。Home组件和Header组件没有关联。需要使用 Vuex 插件,集中式存储管理应该的所有组件的状态,并以相应的规则保证状态以一种可预测的方式变化。

三、vue-cli项目中集成Vuex

1、npm安装vuex

  vue-cli构建的项目中不包含vuex,手动引入:

$ npm install vuex -S

2、vuex入口文件配置

  安装 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对象

3、main.js中引入vuex的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来获取当前的用户对象。

4、Login组件中将用户信息保存到state中

  前面已经在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);
})
},

5、Header组件中监听用户信息更新信息显示

  页首组件在用户没有登录是会显示登录、注册按钮。在用户注册后应显示用户名及用户头像等信息。

(1)实时监听userInfo数据

<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对比:

  1. computed 属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;
  2. methods 方法表示一个具体的操作,主要书写业务逻辑;
  3. watch 一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是 computed 和 methods 的结合体;

(2)template中修改登录前后模板显示

  判断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>
&nbsp;| &nbsp;
<span>注册</span>
</div>

(3)登录后页面显示如下所示

  

6、鼠标悬浮显示/隐藏下拉框

  使用v-show来控制无序列表 <ul> 标签的显示和隐藏。

(1)设置 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>

(2)鼠标悬浮显示ul

  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>
&nbsp;| &nbsp;
<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 的导航守卫来保存用户始终登录。

1、在main.js中引入路由全局守卫

// 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是到哪里去的路由(即将要进入的目标路由对象)。

2、分发Action获取用户信息

  在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变化,因此刷新页面或者跳转到导航栏其他页面时,都会正常显示用户登录信息。

前端Vue项目——登录页面实现的更多相关文章

  1. 前端Vue项目——购物车页面

    一.加入购物车的两种策略 1.加入购物车接口 在 src/restful/api.js 中写入添加购物车接口: // 加入购物车的接口 export const shopCart = (params) ...

  2. 用vue实现登录页面

    vue和mui一起完成登录页面(在hbuilder编辑器) <!DOCTYPE html> <html> <head> <meta charset=" ...

  3. 团队开发前端VUE项目代码规范

    团队开发前端VUE项目代码规范 2018年09月22日 20:18:11 我的小英短 阅读数 1658   一.规范目的: 统一编码风格,命名规范,注释要求,在团队协作中输出可读性强,易维护,风格一致 ...

  4. vue项目刷新页面,使数据不丢失(sessionStorage、localStorage、cookie)

    vue项目刷新页面时,存储在vuex中的数据会丢失,把他们存到stroage中可以保证不丢失.

  5. 前端Vue项目——首页/课程页面开发及Axios请求

    一.首页轮播图 1.elementUI走马灯 elementUI中 Carousel 走马灯,可以在有限空间内,循环播放同一类型的图片.文字等内容. 这里使用指示器样式,可以将指示器的显示位置设置在容 ...

  6. 前端Vue项目——课程详情页面实现

    一.详情页面路由跳转 应用 Vue Router 编程式导航通过 this.$router.push() 来实现路由跳转. 1.绑定查看详情事件 修改 src/components/Course/Co ...

  7. VUE项目实现页面跳转

    打开一个VUE项目,目录结构是这样的: 如现在有两个页面aaa和HelloWorld,路由配置在index.js中: import Vue from 'vue' import Router from ...

  8. 前端vue项目部署到tomcat,一刷新报错404解决方法

    公司前端写的后台部署到tomcat webapps目录下后,无法进行刷新,一刷新就会报错404,自动跳的404页面.在网上查了下,官方说是HTML5 History 模式引发的问题,但是解决方案中,并 ...

  9. 前端Vue项目——初始化及导航栏

    一.项目初始化 创建webpack模板项目如下所示: MacBook-Pro:PycharmProjects hqs$ vue init webpack luffy_project ? Project ...

随机推荐

  1. golang基础之工程结构

    Golang 工作空间 编译工具对源码目录有严格要求,每个工作空间 (workspace) 必须由 bin.pkg.src 三个目录组成. workspace | +--- bin // go ins ...

  2. HTML连载31-制作一个百度首页

    一. 我们制作一个百度首页作为练习,可直接复制该代码保存后缀名为.html来查看 <!DOCTYPE html> <html lang="en"> < ...

  3. 大话设计模式Python实现-模板方法模式

    模板方法模式(Template Method Pattern):定义一个操作中的算法骨架,将一些步骤延迟至子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 下面是一个模 ...

  4. COMP 2406 – F19

    COMP 2406 – F19 – A4 Due Friday, November 22nd at 11:59 PMAssignment 4 Trivia Quiz BuilderSubmit a s ...

  5. 第二十一节:Asp.Net Core MVC和WebApi路由规则的总结和对比

    一. Core Mvc 1.传统路由 Core MVC中,默认会在 Startup类→Configure方法→UseMvc方法中,会有默认路由:routes.MapRoute("defaul ...

  6. QT+OpenGL(03)--libpng库的编译

    1.zlib库的下载 http://www.zlib.net/ zlib1211.zip 2.libpng库的下载 https://libpng.sourceforge.io/index.html l ...

  7. ASP.NET MVC EF 连接数据库(一)-----Database First

    database first (VS2015 ,Sql Server2014) 1,新建MVC项目 实例:   源码代码:http://note.youdao.com/noteshare?id=1fd ...

  8. 解决 Windows Server 2008 R2 上 Windows Update 无法失败,提示 8024402F

    1.同步服务器时间 2.打开 services.msc,停止 Windows Update Service 3.手动下载 KB3138615 补丁:https://support.microsoft. ...

  9. IPv6地址类型和操作

    IPv6地址的号段划分和前缀表示法: IPv6拥有128位巨大的地址空间,对于那么大的空间,也不是随意的划分,而是使用按照bit位进行号段划分 地址结构图 全局路由前缀 (48位) 子网ID (16位 ...

  10. 用for循环数组去重

    <script> var arr=["a","e","w","f","a"," ...