登入登出篇

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 會移除預設事件)
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
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,來幫我們驗證登入狀態。

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:決定是否"放行"到某個路由
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 屬性的值,來決定是否在登入狀態下。

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 的頁面。

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
<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 陣列來完成。

{
  name: 'Dashboard',
  path: '/admin',
  component: Dashboard,
  children: [{
    name: 'Products',
    path: 'products',
    component: Products,
    meta: {
      requiresAuth: true
    },
  }],
},

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

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

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