首先說一下現在項目搭建的程度;
vue-cli 創建的項目
引入了 iview 組件庫,安裝好了 less,安裝了 vue-router 路由;
配置好了跳轉到後臺和登錄頁面的路由;
使用 iview做好了登錄頁面和後臺頁面的框架,如圖:
後臺是需要登錄之後才能訪問的,所以要對訪問進行攔截;
1. 標識一下需要攔截的頁面
一個網站有很多頁面,一些是需要登錄過後才能訪問的,一些是不用登錄也能訪問的;
所以我們要在路由中對需要登錄才能訪問的頁面進行一下標識,以區分這兩者;
標識 meta
// meta 字段中 auth_login 爲 true 的需要進行登錄攔截檢測;
{
path: '/admin',
component: Admin,
meta: {
auth_login: true
},
children: [
{
path: 'user',
component: adminUser,
meta: {
auth_login: true
},
},
{
path: 'role',
component: adminRole,
meta: {
auth_login: true
},
}
]
},
2. 路由攔截器
路由守衛也就是路由攔截器,通過路由攔截器在每一次路由跳轉的時候都做一下判斷,是否需要驗證登錄;
後續再判斷是否登錄成功,這裏先用 true
代替;
const router = new vueRouter({...});
// 前置守衛--在進入新的路由前被攔截 路由攔截器
router.beforeEach((to, from, next)=>{
/**
* 1. to 到哪裏去(路由對象)
* 2. from 從哪裏來(路由對象)
* 3. next 是否允許通行(函數)
*
* 查看 to中有沒有需要攔截的標識,判斷是否攔截
* 不需要攔截的話直接 next() 通行;
* 需要攔截的話,判斷是否登錄,如果登錄了則通行,沒登錄則攔截;
*/
// 是否需要攔截
if(to.meta.auth_login){
// 需要攔截 -- 判斷是否登錄
if(true){
next();
}else{
next({path: '/login'})
}
}else{
// 不需要攔截 -- 通行
next();
}
})
3. 請求
有些時候 前端頁面和服務接口不在同一個服務器,如果通過 ajax 請求的話是會碰到跨域問題的;
而在 webpack
服務器中有一個 代理轉發請求 的功能可以解決這個問題;就是我們請求自己的服務器,由服務器去請求數據,因爲跨域是瀏覽器限制的,在 nodejs
是沒有限制的;
如:
前端地址:
http://localhost:8080/login
服務器地址:
http://console.ranyunlong.com:8080/renren-fast/swagger/
大體流程就是:
我們從瀏覽器請求開發服務器 (webpack-dev-server
),開發服務器請求真實的服務器,真實的服務器把結果返回給開發服務器,開發服務器再返回給瀏覽器;
在開發服務器中配置規則:
vue-cli 3.0 中配置 webpack
的方式:
1)新建文件 vue.config.js
2)進行配置
https://www.webpackjs.com/configuration/dev-server/#devserver-proxy
【vue.config.js】
module.exports = {
devServer: {
proxy: {
"/api": {
// 轉發的目標地址
target: "http://console.ranyunlong.com:8080",
// 路徑重寫
pathRewrite: {
"^/api": "/renren-fast"
}
}
}
}
}
講解:
真實路徑:
http://console.ranyunlong.com:8080/renren-fast/sys/login
但是如果我們訪問這個路徑就跨域了,所以我們要訪問我們的開發服務器:
http://localhost:8080/api/sys/login
經過如上規則配置就相當於訪問了真實路徑;
當我們訪問的路徑有 /api
時,就匹配了 devServer.proxy
的規則,進入了我們寫的這條規則;
就會把我們的 http://localhost:8080
替換成 target
的目標地址http://console.ranyunlong.com:8080
變成:
http://console.ranyunlong.com:8080/api/sys/login
再經過路徑重寫,將 /api
替換成 /renren-fast
,
所以訪問到的路徑就是:
http://console.ranyunlong.com:8080/renren-fast/sys/login
3)測試配置是否生效
重啓服務器
訪問路徑:http://localhost:8080/api/captcha.jpg
返回:
{"msg":"uuid不能爲空","code":500}
和訪問真實的服務器返回的數據是相同的,配置成功;
4. 驗證碼
因爲已經做了代理了,所以可以直接訪問到 服務器的數據,驗證碼是通過 get
方式請求的,這裏直接寫就好了
<img src="/api/captcha.jpg?uuid=0101010" alt="">
但是這個的這個 uuid
只能用一次,這個問題可以通過模塊 uuid
來解決;
# 安裝
npm i uuid
// 導入
// uuid()會生成一個字符串
import uuid from 'uuid'
// 計算屬性拼接成驗證碼路徑;
// 沒有在計算屬性直接使用 uuid() 生成路徑是因爲後面要點擊驗證碼換一張驗證碼,計算屬性的值沒法設置,寫到 data方便後續更新;
export default {
data() {
return{
uid: uuid()
}
},
computed: {
codeUrl(){
return '/api/captcha.jpg?uuid=' + this.uid;
}
}
}
<!--綁定 計算屬性-->
<img :src="codeUrl" alt="">
點擊驗證碼,生成新的驗證碼
<img @click="newCodeUrl()" :src="codeUrl" alt="">
methods:{
newCodeUrl(){
this.uid = uuid();
}
},
computed: {
codeUrl(){
return '/api/captcha.jpg?uuid=' + this.uid;
}
},
data() {
return{
uid: uuid()
}
},
5. 驗證表單數據
步驟見博客:
6. 表單提交
表單提交,是點擊登陸按鈕,然後檢測表單數據,檢測通過之後,進行 ajax 提交;
登錄按鈕
<Button @click="loginFn()" long>login</Button>
登錄按鈕 點擊事件
loginFn(){
// this.$refs['loginForm'] 表單對象
this.$refs['loginForm'].validate((val)=>{
/**
* val true: 效驗成功
* false: 效驗失敗
*/
});
}
axios
在這裏,我們提交沒有使用 ajax
而是使用了 axios
模塊;
文檔:https://www.kancloud.cn/yunye/axios/234845
1)安裝 axios
npm install axios
2)新建文件夾 src/api/
3)新建文件 src/api/http.js
在這裏使用自定義配置新建一個 axios
實例
// 導入 axios模塊
import axios from 'axios'
// 創建一個請求模板 新建一個axios 實例
const http = axios.create({
// 設置基礎路徑
baseURL: '/api',
})
// 攔截請求的 每次請求都會經過這個方法
http.interceptors.request.use((config) => {
console.log(config);
// 可以在這裏處理所有的 請求錯誤
return config;
})
// 攔截響應的 每次收到響應都會經過這個方法
http.interceptors.response.use((response)=>{
console.log(response);
// 可以在這裏處理所有的 響應錯誤
return response;
})
// 暴露出去
export default http;
4) 登錄接口 src/api/login.js
導入新建的 axios
實例,然後執行 post
請求;
/**
* 登錄接口
*/
import http from "./http";
function login(data){
return http.post('/sys/login', data);
}
export default login;
5)在頁面中調用這個登錄接口,傳入數據
<Button @click="loginFn()" long>login</Button>
import login from '../api/login'
_________
data(){
return {
uid: uuid(),
userInfo: {
username: '',
password: '',
captcha: ''
},
}
},
methods:{
loginFn(){
// this.$refs['loginForm'] 表單對象
this.$refs['loginForm'].validate((val)=>{
/**
* val true: 效驗成功
* false: 效驗失敗
*/
if(val) {
login({
...this.userInfo,
uuid: this.uid
})
}
});
}
},
6)通過響應消息判斷請求是否成功
請求成功:
存儲成功憑據:localStorage
跳轉到後臺頁面: this.$router.push('/admin')
請求失敗:
彈窗提示:iview 的全局提示
刷新驗證碼:this.uid = uuid();
loginFn(){
// this.$refs['loginForm'] 表單對象
this.$refs['loginForm'].validate((val)=>{
/**
* val true: 效驗成功
* false: 效驗失敗
*/
if(val) {
login({
...this.userInfo,
uuid: this.uid
}).then((response)=>{
// response 響應過來的信息
// response.data.code === 0 請求成功
const {code, msg, token} = response.data;
if(code === 0) {
// 請求成功,存儲成功信息
localStorage.setItem('token', token)
// 路由跳轉
this.$router.push('/admin');
}else {
// iview 的全局提示
this.$Message.error('登陸失敗:' + msg);
// 刷新驗證碼
this.uid = uuid();
}
})
}
});
}
在路由攔截器中,判斷是否有登陸憑證,有的話通行;
const loginResult = localStorage.getItem('token');
// 前置守衛--在進入新的路由前被攔截 路由攔截器
router.beforeEach((to, from, next)=>{
/**
* 1. to 到哪裏去(路由對象)
* 2. from 從哪裏來(路由對象)
* 3. next 是否允許通行(函數)
*
* 查看 to中有沒有需要攔截的標識,判斷是否攔截
* 不需要攔截的話直接 next() 通行;
* 需要攔截的話,判斷是否登錄,如果登錄了則通行,沒登錄則攔截;
*/
// 是否需要攔截
if(to.meta.auth_login){
// 需要攔截 -- 判斷是否登錄
const loginResult = localStorage.getItem('token');
if(loginResult){
next();
}else{
next({path: '/login'})
}
}else{
// 不需要攔截 -- 通行
next();
}
})
至此:登陸成功