iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 27
1
Modern Web

RRR撞到不負責之 Laravel + Nuxt.js 踩坑全紀錄系列 第 27

Day 27. 居家旅行、殺人滅口,必備良藥 -「註冊」、「登入」與「登出」

  • 分享至 

  • xImage
  •  

差點就要開天窗了...,今天整理之前的介紹,實際用 Laravel 和 Nuxt 做了最雛形的網站包含了「註冊」、「登入」和「登出」功能,有興趣的鐵人大大可以到 GitLab 看完整的 code ! 今天主要是 Nuxt 部分的介紹。

  1. 一開始我們先準備好下列的 page components:

    page component 預期功能

    index.vue | 除了做為系統官方首頁,同時對於登入者而言,也用來導向正確的角色 (管理者/一般用戶) 系統首頁
    register.vue | 註冊一般用戶頁面,如果目前已經是登入狀態則導向首頁
    login.vue | 登入頁面,如果目前已經是登入狀態則導向首頁

    register.vuelogin.vue 當中,除了昨天介紹過的表單寫法之外,主要的重點在 asyncData 的部份,當頁面載入的時候要先檢查是否已經存有 token (即登入的狀態),若已經登入就要跳轉至首頁:

        async asyncData({ store, redirect }) {
            const hasToken = store.getters['hasToken'];
            if (hasToken) {
                redirect('/');
            }
    
            // register 和 login 註冊的表單
        },
    

    login.vueasyncData 裡面,用來判斷受否已經登入以及登入者的角色並用來跳轉頁面:

    export default {
      name: 'index',
      async asyncData({ app, store, redirect }) {
        const hasToken = store.getters.userToken;
        if (hasToken) {
          const isManager = store.getters['user/isManager'];
          const isUser = store.getters['user/isUser'];
          if (isManager) {
            redirect('/manager');
          }
          if (isUser) {
            redirect('/user');
          }
        }
        return {};
      }
    }
    
  2. 剛剛上面有用到 Vuex store module 的資料,所以建立下面的 store module:

    module 資料類型

    index.js | 存放需要記錄在 cookie 的資料
    user.js | 存放登入者的使用資訊

    為了方便控管,同時也是藉由 nuxtServerInit 方法儲存 cookie 相關資料。在今天的例子只有 userToken 一個資料:

    const state = () => {
        return {
            userToken: undefined,
        };
    };
    
    const actions = {
        nuxtServerInit({ commit }, { req }) {
            const hasCookieInReq = !!req.headers.cookie;
            if (hasCookieInReq) {
                try {
                    const allCookies = cookieparser.parse(req.headers.cookie);
                    const token = allCookies['auth-token'];
                    commit('SET_USER_TOKEN', token);
                } catch (error) {
                    // ...
                }
            }
        },
    
        // ...
    

    user.js 的 getters 中,除了取得儲存的 user 同時也特別增加了 hasUser,這是用來確認目前的 store 裡面有沒有資料,假若頁面可以取得 token 但卻沒有 user 資料,代表 Vuex 的資料揮發了,需要透過 API 重新取得資料。

    const state = () => {
        return {
            user: {
                name: undefined,
                email: undefined,
                role: undefined,
            },
        };
    };
    
    // ... 
    
    const getters = {
        hasUser(state) {
            return !!state.user.role;
        },
        // ...
    };
    
  3. 由於任一個 page component 都可能受到 Vuex 資料揮發的影響,因此我們在 middleware 底下加入 auth.js,並且將它掛在 nuxt.config.jsrouter 屬性下,這樣載入任何一頁都會先檢查是否存在 「有 userToken 但是沒有 user」 的情況:

    import { validators } from '@/configs/api/user';
    
    export default async ({ app, store }) => {
        const hasToken = store.getters.hasToken;
        const hasUser = store.getters['user/hasUser'];
    
        if (hasToken) {
            if (!hasUser) {
                try {
                    const { data } = await app.$api(validators.AccessUserRequest, { userId: null });
                    await store.dispatch('user/setUser', data);
                } catch (error) {
                    await store.dispatch('removeUserToken');
                }
            }
        }
    };
    

    順帶一提,因為 JWT 是有時效性的,所以假若 token 已經過期,無法取得 user 資料時,我們會將 userToken 清除,讓使用者可以再次登入。

  4. 至目前為止「註冊」和「登入」功能完成了,但「路由依角色跳轉」功能只完成一半 (首頁的部份),我們很常會直接打上特定網址瀏覽頁面,所以另一半是要撰寫在 pages/manager 底下的頁只有管理者可以瀏覽,pages/user則相反,所以我們增加下列資檔案:

    • page components:
    neast page component 功能

    manager.vue | 將所有 pages/manager 底下的 page components 共同需要的設定即畫面設定於此
    user.vue | 將所有 pages/user 底下的 page components 共同需要的設定即畫面設定於此

    目前上述兩者只需要設定 middleware 即可。

    • middlewares:
    middleware 功能

    managerCheck.js | 檢查 Vuex store user module 裡面儲存的角色是否為管理者
    userCheck.js | 檢查 Vuex store user module 裡面儲存的角色是否為一般使用者

    在上述兩個 middlewares 中,我們都只檢驗個別角色而已,同時只要有不符合的情況一律轉到 pages/index.vue,這樣假設未來系統角色變多了,實際判斷要跳轉的角色首頁都在 pages/index.vue 做就可以了。

    export default async ({ store, redirect }) => {
        const hasToken = store.getters.hasToken;
        const isUser = store.getters['user/isUser'];
    
        if (!(hasToken && isUser)) {
            redirect('/');
        }
    };
    
  5. 最後我們來補上「登出」功能就可以收工啦! 因為不論是管理者或是一般使用者都要有登出功能,在 pages/manager.vuepages/user.vue 各寫一次也可以,但同樣的假設未來角色變多就很麻煩,所以我們可以把「登出」寫在 layouts,這樣在 neast page components 要修改的幅度就少很多了!

    下面是 layout/baseLayout.vue

    <template>
        <Row class="base-layout">
            <Row class="head">
                <!-- ... -->
                <button class="logout-btn" @click="logoutAction">登出</button>
            </Row>
            <Row class="body">
                <nuxt />
            </Row>
        </Row>
    </template>
    
    <script>
    import { mapActions } from 'vuex';
    import { validators } from '@/configs/api/user';
    
    export default {
        name: 'BaseLayout',
        data() {
            return {
                storeUser: undefined,
            };
        },
        methods: {
            ...mapActions(['removeUserToken']),
            async logoutAction() {
                try {
                    const { status, code } = await this.$api(validators.LogoutRequest, {});
                    const successLogout = status && code === 200;
                    if (successLogout) {
                        this.removeUserToken();
                        this.redirectToIndex();
                    }
                } catch (error) {
    
                }
            },
            redirectToIndex() {
                this.$router.push({ name: 'index' });
            }
        },
        async asyncData({ store }) {
            const storeUser = store.getters['user/storeUser'];
            return {
                storeUser,
            }
        }
    }
    </script>
    

    pages/manager.vuepages/user.vue 各加入 layouts 的設定:

        // ...
        layout: 'baseLayout',
    }
    

以上就是「註冊」、「登入」和「登出」搭配多角色的權限控管以跳轉範例,大家可以玩玩看。 今天的範例畫面都醜到炸,明天介紹 SCSS 的基本應用跟小弟個人的小經驗,至少有一些基礎之後不會被說工程師都不懂美感 XD


上一篇
Day 26. 手把手造個輪子 - Form 表單 (文長慎入)
下一篇
Day 28. 別鬧了,定義個樣式 class name 寫得有夠長 - SCSS 基礎介紹
系列文
RRR撞到不負責之 Laravel + Nuxt.js 踩坑全紀錄31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言