javascript --- [express+ vue2.x + elementUI]登陸的流程梳理

說明

涉及到以下知識點:

  • 登陸的具體流程
  • express、vue2.x、elementUI、axios、jwt、assert 登陸方面的API使用
  • 中間件的使用
  • 前后端通過http狀態碼,進行響應的操作(這里主要是401)
  • 密碼驗證(bcrypt的hashSync方法對明文密碼進行加密,compareSync方法對加密的密碼進行驗證)
  • token的使用(后端jwt.sign生成token, 前端使用瀏覽器緩存存儲token,后端驗證token)

登錄

界面

使用Vue2.x + elementUI實現的登錄界面

<template><div class="login-container"><el-card header="請先登錄" class="login-card"><!-- @submit.native.prevent阻止表單的默認提交行為,并將提交的事件處理函數,綁定到login上 --><el-form @submit.native.prevent="login"><el-form-item label="用戶名"><el-input v-model="model.username"></el-input></el-form-item><el-form-item label="密碼"><el-input type="password" v-model="model.password"></el-input></el-form-item><el-form-item><el-button type="primary" native-type="submit">登錄</el-button></el-form-item></el-form></el-card></div>
</template>

邏輯

  • 前端傳入用戶名和密碼
<template><el-form @submit.native.prevent="login"><el-form-item label="用戶名"><el-input v-model="model.username"></el-input></el-form-item><el-form-item label="密碼"><el-input v-model="model.password" type="password"></el-input></el-form-item></el-form>
</template>
<script>export default {data(){return {model: {}}},methods:{async login(){// post提交數據,返回的是一個tokenconst res = await this.$http.post('login', this.model)}}}
</script>
  • 后端得到數據后,與數據庫進行對比
    • 根據用戶名,查找數據庫中的用戶信息
    • 對密碼使用bcrypt的對比操作
  • 通過驗證后,返回一個token
app.post('/admin/api/login', async(req, res)=>{const { username, password } = req.bodyconst AdminUser = require('../../models/AdminUser')// 根據用戶名 查找數據庫中的用戶信息const user = await AdminUser.findOne({username}).select('+password')if(!user) return res.status(422).send({message:'用戶名不存在'})// 走到這里,用戶查找到了const isValid = password && user.password && require('bcrypt').compareSync(password, user.password)if(!isValid) return res.status(422).send({ message:'密碼錯誤'}) // 到這里就使用jwt生成token并返回了// 首先需要安裝: npm i jsonwebtokenconst token = jwt.sign({id: username._id}, app.get('secret'))res.send({token})
})

注意: 上面的jwt.sign接收2個參數,第一個應該是作為區分的一個對象.如用戶的id. 第二個是一個密鑰字符串.在express中使用app.set(‘secret’, ‘Marron’),將密鑰保存在全局app中. 之后使用app.get(‘secret’)獲取該密鑰.

jwt.sign返回一個散列值,就是我們需要的token

異常處理

http響應并不總是成功的,比如在登錄這一塊.用戶名或密碼很容易不成功.這個時候就會返回一個403 forbidden,其中一個比較好的解決方案是: 使用axios的攔截器,對http的返回進行攔截. 若發生錯誤,使用Vue原型上面的$message.error(前面已經使用elementUI掛載了)方法給出錯誤提示

// admin/src/http.js
import axios from 'axios'
import Vue from 'vue'const http = axios.create({baseURL: 'http://localhost:3000/admin/api'
})// 攔截返回的http
http.interceptors.response.use(res=>{// http響應是成功的return res},err=>{if(err.message.response.data.message){// http響應失敗.使用Vue原型上面的方法彈出錯誤Vue.prototype.$message.error({type: 'error',message: err.message.response.data.message})return Promise.reject(err)}}
)

前端得到token

  1. 前端得到token中,首先將其保存在瀏覽器緩存中(sessionStorage或localStorage)
  2. 然后跳轉到首頁,在vue中使用this.$router.push('/')
  3. 彈出提示框,登錄成功, 在vue2.x + elementUI中使用this.$message({ type:'success', message:'登錄成功'})
<template><el-form @submit.native.prevent="login"><el-form-item label="用戶名"><el-input v-model="model.username"></el-input></el-form-item><el-form-item label="密碼"><el-input v-model="model.password" type="password"></el-input></el-form-item></el-form>
</template>
<script>export default {data(){return {model: {}}},methods:{async login(){// post提交數據,返回的是一個tokenconst res = await this.$http.post('login', this.model)// 將token存入localStorage中(前端磁盤),瀏覽器關閉了,下次還能訪問到localStorage.token = res.data.token// 跳轉到首頁this.$router.push('/')// 彈出登錄成功this.$message({type: 'success',message: '登錄成功'})}}}
</script>

登錄驗證

前端請求添加token

如果用戶登錄了,那么在瀏覽器的localStorage中必然會保存token.在發送登錄請求給后端時,需要帶上這個token.在axios中使用全局的請求攔截(http.interceptors.request.use)來實現這個功能

const http = axios.create({baseURL: 'http://localhost:3000/admin/api'
})
http.interceptors.request.use(config => {if(localStorage.token) {// 給請求頭部加tokenconfig.headers.Authorization = 'Bearer ' + localStorage.token}return config},err => {return Promise.reject(err)}
)
// 在使用`config.headers.Authorization`之后,每次http請求都會附帶一個 Authorization 請求頭.
// Authorization值的最前面加 'Bearer '的原因是為了符合規范

在添加好了對所有路由的前端請求攔截后,如果因為登錄原因,出現的錯誤.后端一般返回的是401狀態碼,這個時候,需要跳轉到登陸頁面,下面對狀態碼 401 的返回值進行處理

const http = axios.create({baseURL: 'http://localhost:3000/admin/api'
})
http.interceptors.response.use(res => { return res},err => {// 這里處理報錯if(err.response.data.message){Vue.prototype.$message.error({type: 'error',message: err.response.data.message})// 處理401狀態碼: 跳轉到登陸頁面if(err.response.status == 401) router.push('/login')}}
)

后端解析請求頭,驗證token

以上實現了前端在發送http請求時,攜帶token(放在Authorization請求頭部中),后端需要在前端通過URL請求非登陸接口時,判斷用戶是否登錄.可以寫一個登陸驗證的中間件. 如果在對正常的路徑進行處理之前,先通過中間件驗證.

中間件的邏輯如下:

  • 首先通過req.headers.authorization獲取token,若無token則設置為 ''
    • 若token存在則繼續下一步,否則使用assert拋出異常
  • 然后使用jwt驗證token.得到解碼后的id
    • 若存在id,則證明token沒有被篡改,繼續下一步,否則assert拋出異常
  • 最后根據id在數據庫中查詢user.并將user賦給req.user.
    • 若存在req.user則跳轉到下一個中間件,否則使用assert拋出異常
// sever/middleware/auth.js
module.exports = options =>{const assert = require('http-assert') // 簡化代碼const jwt = require('jsonwebtoken')	// 用于將token密文解析為明文const AdminUser = require('../models/AdminUser') // 獲取AdminUser模型return async(req, res, next)=>{// 獲取tokenconst token = String(req.headers.authorization || '').split(' ').pop()assert(token, 401, '請先登陸')	// 這里若無token: 則代表未登陸const {id} = jwt.verify(token, req.app.get('secret'))assert(id, 401, '請先登陸')  // 這里若無id: 則代表token過期或被篡改了req.user = AdminUser.findById(id)assert(req.user, 401, '請先登錄') // 這里若沒用找到user, 則代表給的是假idawait next()}
}

以上提供了一個中間件: 它判斷token是否存在(正確),若不正確則拋出異常.若正確則繼續下一個中間件.在主路由中導入.并在需要的地方添加這個中間件

// server/router/admin/index.js
module.exports = app => {const express = require('express')const authMiddleware = require('../../middleware/auth')const router = express.router({mergeParams: true})app.use('/admin/api/rest/:resource', authMiddleware(), router)
}

上面使用了assert拋出異常,因此還需要一個錯誤捕捉中間件,放在所有路由的最后,用于捕捉錯誤,它會將捕獲到的異常,作為參數傳遞給前端.這樣前端就能根據狀態碼做出響應的處理了

// server/router/admin/index.js
module.exports = app => {const express = require('express')const authMiddleware = require('../../middleware/auth')const router = express.router({mergeParams: true})// 放到所有路由的后面app.use(async (err, req, res, next)=>{console.log(err)res.status(err.statesCode || 500).send({ message: err.message})})
}

前端路由校驗

上面已經完成了,在發送Http請求時,進行路由校驗.若401則跳轉到登陸頁面.但是,那些不需要HTTP請求的網頁還是可以不需要登陸就能直接訪問.下面有必要做前端的路由校驗

【具體實現如下】:

  • 將不需要登陸的就能訪問的(主要是是Login組件)路由,添加一個{isPublic: true}屬性
  • 然后使用全局前置守衛,在進入路由前判斷localStorage中是否存在token.若存在,則進行下一步,否則跳轉到登陸頁面. 而登陸頁面設置了公開訪問屬性,因此不會觸發全局前置守衛
// admin/src/router/index.js
import Vue from 'vue'
import VurRouter from 'vue-router'
const routes = [{ path: '/login',name: 'login',component: Login,meta: { isPublic: true}},{path: '/',name: 'main',component: Main,children: [ ... ]}
]
const router = new VueRouter({routes      
})router.beforeEach((to, from, next) =>{if(!to.meta.isPublic && localStorage.token){return next('/login')}next()
})
export default router

在主函數中加載路由,并渲染更新

// admin/src/main.js
import Vue from 'vue'
import App from './App.vue'
import './plugins/element.js'
import router from './router'import './style.css'Vue.config.productionTip = falsenew Vue({router,render: h => h(App)
}).$mount('#app')

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/250250.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/250250.shtml
英文地址,請注明出處:http://en.pswp.cn/news/250250.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

設計模式---裝飾模式

今天學習了裝飾模式&#xff0c;做個筆記。。裝飾模式的基礎概念可以參考&#xff1a;https://blog.csdn.net/cjjky/article/details/7478788 這里&#xff0c;就舉個簡單例子 孫悟空有72變&#xff0c;但是它平時是猴子&#xff0c;遇到情況下&#xff0c;它可以變成蝴蝶等等 …

springMvc 注解@JsonFormat 日期格式化

1&#xff1a;一定要加入依賴,否則不生效&#xff1a; <!--日期格式化依賴--><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>${jackson.version}</version>&…

Git很簡單--圖解攻略

Git Git 是目前世界上最先進的分布式版本控制系統&#xff08;沒有之一&#xff09;作用 源代碼管理為什么要進行源代碼管理? 方便多人協同開發方便版本控制Git管理源代碼特點 1.Git是分布式管理.服務器和客戶端都有版本控制能力,都能進行代碼的提交、合并、. 2.Git會在根…

css --- 使用scss生成常用的基本css樣式

"工具樣式"的概念和 SASS(SCSS) 在webpack中使用sass 安裝sass和sass-loader $ npm i sass sass-loader由于使用了腳手架,安裝完畢后重啟前端即可 樣式重置 其實就是樣式的初始化 // reset* {box-sizing: border-box; // 以邊框為準. css3盒模型outline: none;…

vc/vs開發的應用程序添加dump崩潰日志轉

原貼地址&#xff1a;https://blog.csdn.net/wangkui1331/article/details/78029940 vc/vs開發的應用程序出現崩潰的時候&#xff0c;由于沒有任何記錄&#xff0c;導致開發人員很難追蹤&#xff0c;但是添加dump文件后&#xff0c;就可以免除這些煩惱 1.添加方法 &#xff08;…

51 nod 1127最短的包含字符串(尺取法)

1127 最短的包含字符串 收藏關注給出一個字符串&#xff0c;求該字符串的一個子串S&#xff0c;S包含A-Z中的全部字母&#xff0c;并且S是所有符合條件的子串中最短的&#xff0c;輸出S的長度。如果給出的字符串中并不包括A-Z中的全部字母&#xff0c;則輸出No Solution。Input…

Java --- 基礎學習Ⅰ

第一章 開發前言 位、字節 位(bit): 一個數字0或一個數字1,代表一位 字節(Byte): 每逢8位是一個字節,這時數據存儲的最小單位 1 Byte 8 bit 1 KB 1024 Byte 1 MB 1024 KB 1 GB 1024 MB 1 TB 1024 GB 1 PB 1024 TB MS-DOS(Microsoft Disk Operating System) 第二章 Ja…

JSON 數據重復 出現$ref

JSONArray 類型 如果我們往里面add數據的時候 如果數據相同&#xff0c;那么就會被替換成 $ref: 也就是被簡化了 因為數據一樣所直接 指向上一條數據 循環引用&#xff1a;當一個對象包含另一個對象時&#xff0c;fastjson就會把該對象解析成引用。引用是通過$ref標示的&am…

Java --- 基礎學習Ⅱ

繼承 繼承概述 下面有一個學生類 public class Student{private String name;private int age;public void study(){System.out.println("努力學習了");}public String getName() {return name;}public void setName(String name) {this.name name;}public int g…

urllib庫

python內置的最基本的HTTP請求庫&#xff0c;有以下四個模塊&#xff1a; urllib.request  請求模塊 urllib.error    異常處理模塊 urllib.parse   url解析模塊 urllib.robotparser robots.txt解析模塊 urllib.request請求模塊&#xff1a; urllib.request.urlopen(u…

layer的刪除詢問框的使用

刪除是個很需要謹慎的操作 我們需要進行確認 對了刪除一般使用ajax操作 因為如果同url請求 處理 再返回 會有空白頁 1.js自帶的樣式 <button type"button" data-toggle"tooltip" title"刪除" class"btn btn-danger pull-right btn-xs&qu…

文獻筆記(八)

一、基本信息 標題&#xff1a;MySQL數據庫在自動測試系統中的應用 時間&#xff1a;2017 出版源&#xff1a;寧夏職業技術學院 領域分類&#xff1a;無線互聯科技 二、研究背景 問題定義&#xff1a;文章介紹了MySQL數據庫的特點&#xff0c;結合自動測試系統運行中的實際&…

Java --- 常用API

常用API 方法重載: 方法名相同,方法接收的參數不同 static: 修飾的類,可以直接使用類名進行調用 方法名說明public static abs(int a)返回參數的絕對值public static double ceil(double a)返回大于或等于public static double floor(double a)返回小于或等于參數的最大doubl…

9. 彈出鍵盤擋住input

1.) react 中 <input className"inp3" placeholder"密碼" type"password" onChange{this.changepassword.bind(this)} onFocus{this.FocusFN.bind(this)} value{this.state.paswword}/> FocusFN(){ setTimeout(()>{ let pannel docume…

Linux初學時的一些常用命令(4)

1. 磁盤 查看當前磁盤使用情況 df -h查看某個文件大小 du -sh 文件名 如果不輸入文件名&#xff0c;默認是當前目錄的所有文件之和&#xff0c;即當前目錄大小 2. 系統內存 free參數詳解&#xff1a;https://blog.csdn.net/loongshawn/article/details/51758116 3. CPU CPU 使用…

小程序 --- 項目小練手Ⅰ

1. 接口文檔 2. 幫助文檔 小程序開發文檔 mdn 阿里巴巴字體 iconfont 3. 項目搭建 3.1 新建小程序項目 填入自己的appid: wxdbf2b5e8c2f521a3 3.2 文件結構 一級目錄 目錄名作用styles存放公共樣式components存放組件lib存放第三方庫utils自己的幫助庫request自己的接口…

vue aixos請求json

this.axios.get(/static/json/jinxiangZhe.json).then(res>{console.log(res);}).catch( error > {console.log(error,error)}) 轉載于:https://www.cnblogs.com/SunShineM/p/9087734.html

小程序 --- Tab組件的封裝

1. Tabs組件的封裝 1.1 組件的引入 使用自定義的組件很簡單,只需在使用組件的頁面的配置文件.json文件中配置. // pages/goods_list/index.json {"usingComponents":{"Tabs": "../../components/Tabs/Tabs"} }然后再.wxml文件中使用即可 <…

爬蟲之拉勾網職位獲取

重點在于演示urllib.request.Request()請求中各項參數的 書寫格式 譬如&#xff1a; url data headers...Demo演示&#xff08;POST請求&#xff09;:import urllib.requestimport urllib.parseimport json, jsonpath, csvurl "https://www.lagou.com/jobs/positionAjax.…

小程序 --- 點擊放大功能、獲取位置信息、文字樣式省略、頁面跳轉(navigateTo)

1. 點擊放大功能的實現 需求: 點擊輪播圖中的圖片會實現放大預覽的功能。首先有輪播圖的樣式如下 <!-- pages/goods_detail/index.wxml --> <!-- 輪播圖 --> <view class"detail_swiper"><swiperautoplaycircularindicator-dots><swip…