Vue 初心者筆記 #36 實作登入登出功能

這篇文章是紀錄我實作登入登出功能的流程。除了實作出登入登出行為外,也有做出驗證登入的功能喔!

登入登出篇

Part 1. 登入登出行為

建立環境

  1. 在 components 資料夾下建立 pages 資料夾,並新增 login.vue 分頁元件
  2. 路由檔 (index.js) 也要加上 Login 這個 routes
  3. 基本登入畫面的 HTML 與 CSS 可以使用 Bootstrap 的範本

登入 API

  1. data 新增 user 帳號密碼
  2. 透過 v-model 綁定 user.usernameuser.password
  3. 透過 @submit.prevent="signin" 觸發登入 API(.prevent 會移除預設事件)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
export default {
name: 'HelloWorld',
data() {
return {
user: {
username: '',
password: '',
},
};
},
methods: {
signin() {
const api = `${process.env.APIPATH}/signin`;
const vm = this;
// 把用戶的資料傳進來
// (vm.user = 帳號 & 密碼)
this.$http.post(api, vm.user).then((response) => {
console.log(response.data);
// 回首頁
if (response.data.success) {
vm.$router.push('/'); // 如果登入成功,就會把路徑改到指定的頁面 (首頁)
}
});
},
},
};

登出 API

  • 透過 @click.prevent="signout" 觸發登出 API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
methods: {
signout() {
// 登出的事件
const api = `${process.env.APIPATH}/logout`;
const vm = this;
// 不需要傳入任何參數,只要觸發這個 API 就會直接登出
this.$http.post(api).then(response => {
console.log(response.data);
// 如果成功登出,就回到 Login 頁面
if (response.data.success) {
vm.$router.push("/login");
}
});
}
}

Part 2. 跨域登入驗證

這邊只紀錄前端的部分

前端 axios 請求附帶 Cookies 設定

前端其實只要加上以下片段,就能把 Cookie 正確地存在 Vue 的伺服器內。

  1. 修改 API 路徑
    • 將登入的路徑改為 ${process.env.APIPATH}/admin/signin
  2. 開啟 Cookies 開關 (withCredentials)
    • 在 main.js 加上 axios.defaults.withCredentials = true,就能將 withCredentials 參數設定為 true

加入這兩行有什麼效果呢?
主要功能是後端會自動加上 Session 並自動存在 Cookie 裡面。
這樣把 Cookies 正確存起來之後,使用者才能正確執行切換頁面等操作。

如果想要找 Session 的話,可以在 Console → Application → Cookies 裡面找到

Part 3. 驗證登入

路由元信息 (meta)

在 index.js 中,對於要驗證的頁面的路由上,加上一個判斷基準 requiresAuth,來幫我們驗證登入狀態。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export default new VueRouter({
routes: [
{
name: 'HelloWorld',
path: '/',
component: HelloWorld,
// 判斷基準
meta: { requiresAuth: true },
},
{
name: '登入',
path: '/login',
component: Login,
},
],
});

我們這邊把 requiresAuth 放在 HelloWorld 頁面上。
當我們要從 Login 切換到 HelloWorld 頁面時,待會的 to 裡面的 meta 屬性就會有值。

Vue Router 導航守衛

在 main.js 中,透過 vue-router 提供的導航守衛,來應對路由切換的變化。

  • to:即將要進入的目標路由
  • from:當前正要離開的路由
  • next:決定是否”放行”到某個路由
1
2
3
4
5
6
7
8
9
10
11
12
router.beforeEach((to, from, next) => {
console.log('to', to);
console.log('from', from);
console.log('next', next);
if (to.meta.requiresAuth) {
// 要到的頁面 (to),它的 meta 如果有 requiresAuth 的話,就"不會"直接放行
console.log('這裡需要驗證');
} else {
// 反之,若沒有 requiresAuth 的話,就會直接放行
next();
}
});

這個時候要從 Login 直接切到首頁時就會跳出需要驗證的提醒了。
接下來我們就把 if 裡面的 console.log() 換成驗證相關的程式碼。

檢查用戶是否仍持續登入

綜合以上兩點,我們就能實作出驗證使用者是否為登入狀態的功能。
我們透過驗證 API 所回傳的資訊,即 success 屬性的值,來決定是否在登入狀態下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
router.beforeEach((to, from, next) => {
// 這裡需要驗證
if (to.meta.requiresAuth) {
// 驗證用的 API 路徑
const api = `${process.env.APIPATH}/api/user/check`;
axios.post(api).then((response) => {
if (response.data.success) {
next(); // 如果成功登入就放行
} else {
// 如果不是登入狀態時,就回到登入頁面
next({
path: '/login',
});
}
});
} else {
next();
}
});

第六行因為不是在 Vue App 內,所以要使用 axios 取代 this.$http

Part 4. 重新導向

最後我們要做一個 redirect 的功能,當輸入不存在的頁面網址時,會自動跳回指定的頁面。

redirect

我們在 index.js 中定義一個 path: '*' 的路由,並透過 redirect 轉址到 login 的頁面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
export default new VueRouter({
routes: [
{
path: '*',
redirect: 'login',
},
{
name: 'HelloWorld',
path: '/',
component: HelloWorld,
meta: {
requiresAuth: true,
}, // 判斷基準
},
{
name: '登入',
path: '/login',
component: Login,
},
],
});

因此,只要我們輸入的網址不是我們定義的 path 的話,就會被重新導向到 Login 頁面。
這麼做就能避免用戶進到不存在的頁面,而發生錯誤了。

Part 5. 後台版型

套用 Bootstrap Dashboard 版型

版型部分可以使用 Bootstrap 的範本
首先,在 components 資料夾下新增 Dashboard.vue,並將需要的 HTML 原始碼貼進去。
同理,在 assets 資料夾下新增 _dashboard.scss,並將範本的樣式 直接貼入,並在 all.scss 內 @import 進去。

這邊要特別注意,<template> 內要放 1 個 <div> 在最外層!

最後再 import Dashboard.vue 到路由檔案 (index.js) 中,並新增一個帶 requiresAuth 的路由。
都完成後,就能順利顯示 Dashboard 的頁面了。

新增 requiresAuth 可驗證登入狀態

版型拆解 - 元件化

將 Dashboard 的 Navbar 與 Sidebar 拆解出來,原本的 Dashboard 只留下 Main 來放自己的主要內容。
所以,我們拆解完總共會有以下三個 .vue 檔案:

  • Dashboard.vue:就只是個外框
  • Navbar.vue:上方選單
  • Sidebar.vue:側邊選單

接著我們就可以在 Dashboard.vue 中,把 Navbar 與 Sidebar 這兩個元件 import 進來囉。

Dashboard.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
<div>
<div class="container-fluid">
<Navbar />
<div class="row">
<Sidebar></Sidebar>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
<!-- main 換成自己的內容 -->
</main>
</div>
</div>
</div>
</template>

<script>
import Navbar from "./Navbar";
import Sidebar from "./Sidebar";

export default {
components: {
Navbar,
Sidebar,
}
};
</script>

動態切換載入元件

我們新增一個 Products.vue 元件放在 Main 裡面,這預計會用來做產品列表等內容。
這個 Products.vue 當然也可以用剛才的方式,直接將元件 (component) 嵌入頁面。

但是,這裡我們要試著使用 Router 的方式來設定,因為這樣做的話,我們之後還能動態地切換載入元件。
那麼,如果要動態切換元件,我們就會使用到 <router-view>

routerView

我們在 Dashboard.vue 的 <main> 內放入 <router-view>

<router-view> 是切換主要內容的區域,可以視 <router-view> 以外的區域為固定樣板,而 <router-view> 內就是每個頁面切換的內容。

這個 <router-view> 是巢狀的,因為外層的 App.vue 已經有一個了,所以 Dashboard.vue 的這個 <router-view> 是內層的。

巢狀路由

巢狀的路由 (Router) 在之前有使用過,可以透過 children 陣列來完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
{
name: 'Dashboard',
path: '/admin',
component: Dashboard,
children: [{
name: 'Products',
path: 'products',
component: Products,
meta: {
requiresAuth: true
},
}],
},

要特別注意!在官方的範例中,根路徑通常會加上 /,子路徑則不會加上 /

最後這裡將 meta: { requiresAuth: true } 移到 products 子路由上,確保要驗證登入過後,才能夠進入產品列表的頁面。

以上資源是我自己整理過後的筆記,若有錯誤歡迎隨時和我聯繫。