前端Vue項目——登陸頁面實現

1、geetest滑動驗證

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

  產品——極速驗證:基於深度學習的人機識別應用。極驗「行爲驗證」是一項能夠幫助你的網站與APP識別與攔截機器程序批量自動化操做的SaaS應用。它是由極驗開發的新一代人機驗證產品,它不基於傳統「問題-答案」的檢測模式,而是經過利用深度學習對驗證過程當中產生的行爲數據進行高維分析,發現人機行爲模式與行爲特徵的差別,更加精準地區分人機行爲。前端

一、web部署介紹

  客戶端官方文檔:https://docs.geetest.com/install/deploy/client/web/vue

(1)引入初始化函數

  經過引入 gt.js 文件,引入 initGeetest 初始化函數。ios

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

  注意:行爲驗證要求初始化在業務頁面加載同時初始化,不然驗證沒法讀取用戶在業務頁面操做的行爲數據,致使驗證策略失效。web

  這裏的 gt.js 文件,它用於加載對應的驗證JS庫。在每一個後端語言的sdk中都存有一份,開發者部署到實際環境須要將該文件複製到相應的項目中使用。ajax

  以前該文件地址是 https://static.geetest.com/static/tools/gt.js  ,改成存放在用戶的項目中,防止靜態服務器出問題沒法加載該文件。vuex

(2)調用初始化函數初始化 

  使用初始化函數 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

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

二、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校驗顯示效果

  顯示效果以下所示:

  

2、二次驗證數據處理和登陸實現

一、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);
        })
    }

二、配置登陸接口

  在 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 插件,集中式存儲管理應該的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式變化。

3、vue-cli項目中集成Vuex

一、npm安裝vuex

  vue-cli構建的項目中不包含vuex,手動引入:

$ npm install vuex -S

二、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對象

三、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來獲取當前的用戶對象。

四、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);
      })
  },

 

五、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)登陸後頁面顯示以下所示

  

六、鼠標懸浮顯示/隱藏下拉框

  使用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>    

4、全局守衛保持用戶始終登陸

  前面實現了用戶登陸,Header顯示用戶登陸信息。可是若是刷新頁面或者跳轉到導航欄其餘頁面。會發現Header顯示的不是用戶登陸信息,而是 登陸/註冊。

  所以須要使用Vue-Router 的導航守衛來保存用戶始終登陸。

一、在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是到哪裏去的路由(即將要進入的目標路由對象)。

二、分發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變化,所以刷新頁面或者跳轉到導航欄其餘頁面時,都會正常顯示用戶登陸信息。

相關文章
相關標籤/搜索