Vue項目中實現用戶登錄及token驗證


一、什么是token

      token的意思是“令牌”,是服務端生成的一串字符串,作為客戶端進行請求的一個標識。當用戶第一次登錄后,服務器生成一個token並將此token返回給客戶端,以后客戶端只需帶上這個token前來請求數據即可,無需再次帶上用戶名和密碼。

簡單token的組成;uid(用戶唯一的身份標識)、time(當前時間的時間戳)、sign(簽名,token的前幾位以哈希算法壓縮成的一定長度的十六進制字符串。為防止token泄露)。

二、token的作用

  1、防止表單重復提交:主要的理念是,客戶端初始化的時候(一般就是剛剛進入頁面的時候)就調用后端代碼,后端代碼生成一個token,返回給客戶端,客戶端儲存token(可以在前台使用Form表單中使用隱藏域來存儲這個Token,也可以使用cookie),然后就將request(請求)中的token與(session)中的token進行比較

   2、用來作身份驗證:

(1)身份認證概述

   由於HTTP是一種沒有狀態的協議,它並不知道是誰訪問了我們的應用。這里把用戶看成是客戶端,客戶端使用用戶名還有密碼通過了身份驗證,不過下次這個客戶端再發送請求時候,還得再驗證一下。

  通用的解決方法是:當用戶請求登錄的時候,如果沒有問題,在服務端生成一條記錄,在這個記錄里可以說明登錄的用戶是誰,然后把這條記錄的id發送給客戶端,客戶端收到以后把這個id存儲在cookie里,下次該用戶再次向服務端發送請求的時候,可以帶上這個cookie,這樣服務端會驗證一下cookie里的信息,看能不能在服務端這里找到對應的記錄,如果可以,說明用戶已經通過了身份驗證,就把用戶請求的數據返回給客戶端。

以上所描述的過程就是利用session,那個id值就是sessionid。我們需要在服務端存儲為用戶生成的session,這些session會存儲在內存,磁盤,或者數據庫。

2)基於token機制的身份認證:使用token機制的身份驗證方法,在服務器端不需要存儲用戶的登錄記錄。流程如下:

  1. 客戶端使用用戶名和密碼請求登錄。
  2. 服務端收到請求,驗證用戶名和密碼。
  3. 驗證成功后,服務端會生成一個token,然后把這個token發送給客戶端。
  4. 客戶端收到token后把它存儲起來,可以放在cookie或者Local Storage(本地存儲)里。
  5. 客戶端每次向服務端發送請求的時候都需要帶上服務端發給的token。
  6. 服務端收到請求,然后去驗證客戶端請求里面帶着token,如果驗證成功,就向客戶端返回請求的數據。

三、Vue項目中實現用戶登錄及token驗證

   1、思路如下:

(1) 第一次登錄的時候,前端調后端的登陸接口,發送用戶名和密碼

(2) 后端收到請求,驗證用戶名和密碼,驗證成功,就給前端返回一個token

(3) 前端拿到token,將token存儲到localStorage和vuex中,並跳轉路由頁面

(4) 前端每次跳轉路由,就判斷 localStroage 中有無 token ,沒有就跳轉到登錄頁面,有則跳轉到對應路由頁面

(5) 每次調后端接口,都要在請求頭中加token

(6) 后端判斷請求頭中有無token,有token,就拿到token並驗證token,驗證成功就返回數據,驗證失敗(例如:token過期)就返回401,請求頭中沒有token也返回401

(7) 如果前端拿到狀態碼為401,就清除token信息並跳轉到登錄頁面

   2、后台接口(node.js)的實現

     1)安裝JWT:  npm install jsonwebtoken --save

JWT(Json Web Token)是一種身份驗證及授權方案,簡單的說就是客戶端調用 api 時,附帶上一個由 api 端頒發的 token,以此來驗證調用者的授權信息。

      (2)JWT的常用函數:

         Asign(payload, secretOrPrivateKey, [options, callback])

 

       Bverify(token, secretOrPublicKey, [options, callback])

 

      Cdecode(token [, options])

 

(3)后台代碼實現:

var express = require('express');

var router = express.Router();

var pool = require('../config/blogdb.js')

const jwt = require('jsonwebtoken'); //引入jwt模塊

 

/**

 * http://localhost:8089/blog/login

 */

router.post('/login',function(req,res){

    let username = req.body.username;

    let password = req.body.password;

    console.log("用戶名="+username)

    pool.getConnection(function(err,conn){

      if(err){

          console.log('連接數據庫失敗!')

      }else{

          let data=[username,password]

          let sql = "select * from admin where username= ? and password = ?";

          conn.query(sql,data,function(error,results){

              if(error){

                  console.log(error)

              }

             if(results != null){ //若查詢結果不為空

                const payload = { //定義token的有限載荷

                    name: results.username

                   }

                   const secret = 'deyun' //給定密鑰

                   //定義token

                const token = jwt.sign(payload,secret,{ 

                    'expiresIn':1440});// 設置過期時間

             

                res.json({ //將響應信息轉換為json格式

                    success: true,

                    message: 'Enjoy your token',

                    token: token

                })

            }

              conn.release(); //釋放數據庫連接對象

          })

      }

    });

  })

  module.exports = router;

 

(4)JWT機制優缺點

       優點:

  • 支持跨域訪問: Cookie是不允許垮域訪問的,這一點對Token機制是不存在的,前提是傳輸的用戶認證信息通過HTTP頭傳輸.
  • 無狀態(也稱:服務端可擴展行):Token機制在服務端不需要存儲session信息,因為Token 自身包含了所有登錄用戶的信息,只需要在客戶端的cookie或本地介質存儲狀態信息.
  • 更適用CDN: 可以通過內容分發網絡請求你服務端的所有資料(如:javascript,HTML,圖片等),而你的服務端只要提供API即可.
  • 去耦:不需要綁定到一個特定的身份驗證方案。Token可以在任何地方生成,只要在你的API被調用的時候,你可以進行Token生成調用即可.
  • 更適用於移動應用: 當你的客戶端是一個原生平台(iOS, Android,Windows 8等)時,Cookie是不被支持的(你需要通過Cookie容器進行處理),這時采用Token認證機制就會簡單得多。
  • CSRF:因為不再依賴於Cookie,所以你就不需要考慮對CSRF(跨站請求偽造)的防范。
  • 性能: 一次網絡往返時間(通過數據庫查詢session信息)總比做一次HMACSHA256計算 的Token驗證和解析要費時得多.
  • 基於標准化:你的API可以采用標准化的 JSON Web Token (JWT). 這個標准已經存在多個后端庫(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft)
  • 缺點:
  • JWT 的最大缺點是,由於服務器不保存 session 狀態,因此無法在使用過程中廢止某個 token,或者更改 token 的權限。也就是說,一旦 JWT 簽發了,在到期之前就會始終有效,除非服務器部署額外的邏輯。
  • JWT 本身包含了認證信息,一旦泄露,任何人都可以獲得該令×××的所有權限。為了減少盜用,JWT 的有效期應該設置得比較短。對於一些比較重要的權限,使用時應該再次對用戶進行認證。
  • 為了減少盜用,JWT 不應該使用 HTTP 協議明碼傳輸,要使用 HTTPS 協議傳輸。

三、前端Vue實現

   1、創建項目:vuetokendemo,目錄結構如下

 

 

2、給項目安裝vuex模塊

 

3、main.js文件

import Vue from 'vue'

import App from './App'

import router from './router'

import axios from 'axios' 

 

import store from './store' //導入store

Vue.config.productionTip = false

Vue.prototype.$http = axios 

// 導航守衛

// 使用 router.beforeEach 注冊一個全局前置守衛,判斷用戶是否登陸

router.beforeEach((to,from,next) => {

  if(to.path === '/login'){

    next();

  }else{

    let token = window.localStorage.token;

    if(token === 'null' || token === '' || token === undefined){

      next('/login')

    }else{

      next();

    }

  }

});

//添加請求攔截器

axios.interceptors.request.use(

  config => {

    if(store.state.token){

      config.headers.common['token'] = store.state.token.token

    }

    return config;

  },

  error => {

    //請求錯誤

    return Promise.reject(error);

  }

);

//添加響應攔截器

axios.interceptors.response.use(

  response => {

    return response;

  },

  error => {

    if(error.response){

      switch(error.response.status){

        case 401:

          localStorage.removeItem('token');

          router.replace({

            path: '/login',

            query: {

              redirect: router.currentRoute.fullPath //登錄成功后跳入瀏覽的當前頁

            }

          })

      }

    }

  }

)

new Vue({

  el: '#app',

  router,

  store,

  components: { App },

  template: '<App/>'

})

 

 4、login.vue組件

<template>

  <div>

    <input type="text" v-model="loginForm.username" placeholder="用戶名"/>

    <input type="text" v-model="loginForm.password" placeholder="密碼"/>

    <button @click="login">登錄</button>

  </div>

</template>

 

<script>

import { mapMutations } from 'vuex'

export default {

    data () {

        return {

        loginForm: {

            username: '',

            password: ''

        }

        };

    },

    methods: {

        ...mapMutations(['$_setStorage']),

        login () {

          let _this = this;

          if(this.loginForm.username === '' || this.loginForm.password === ''){

                alert('賬號或密碼不能為空');

            }else{

                this.$http({

                    method: 'post',

                    url: 'http://localhost:8089/blog/login',

                    data: _this.loginForm

                }).then(res=>{

                    console.log(res.data)

                    _this.$_setStorage({Authorization:res.data.token});

                    _this.$router.push('/home');

                    alert('登錄成功')

                }).catch(error=>{

                    alert('賬號或密碼錯誤');

                    console.log(error)

                })

            }

        }

    }

}

</script>

 

5、home.vue組件

<template>

   <h1>首頁,跳轉成功</h1>

</template>

 

<script>

export default {

}

</script>

<style>

</style>

6、store/index.js文件

import Vue from 'vue'

import Vuex from 'vuex'

 

Vue.use(Vuex)

const key = 'token'

const store = new Vuex.Store({

    state () {

        return {

          token: localStorage.getItem('token')?localStorage.getItem('token'):''

        }

    },

    getters: {

        getSortage: function(state) {

            if(!state.token){

                state.token =JSON.parse(localStorage.getItem(key))

            }

            return state.token

        }

    },

    mutations: {

        $_setStorage(state,value){

            state.token = value

            localStorage.setItem(key,JSON.stringify(value))

        },

        $_removeStorage(state){

            state.token = null;

            localStorage.removeItem(key)

        }

    }

})

export default store;

7、router/index.js文件

import Vue from 'vue'

import Router from 'vue-router'

import login from '@/components/login'

import home from '@/components/home'

Vue.use(Router)

 

const router = new Router({

  routes: [

    {

      path: '/', 

      redirect: '/login'

    },

    {

      path: '/login',

      name: 'login',

      component: login

    },

    {

      path: '/home',

      name: 'home',

      component: home

    }

  ]

});

//導航守衛

//使用router.beforeEach注冊一個全局前置守衛,判斷用戶是否登錄

router.beforeEach((to,from,next)=>{

  if(to.path === '/login'){

    next();

  }else{

    let token = localStorage.getItem('Authorization');

    if( token === 'null' || token === ''){

      next('/login')

    }else{

      next();

    }

  }

});

export default router;

 

8、運行效果

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM