單頁應用中登陸狀態保持和頁面訪問權限控制的解決辦法

單頁應用與多頁應用

傳統的 web 項目中,多數是多頁應用,即每次頁面跳轉的時候,後臺服務器都會給返回一個新的 html和與之相關的 cssjs 資源,例如熟悉的 JSP ,這種類型的網站也就是多頁網站,也叫作多頁應用。這種應用最明顯的缺點就是頁面切換的響應速度慢、靜態資源的重複加載等。css

而單頁應用(SPA)在頁面跳轉的時候,並不須要請求新的 htmlcssjs 等資源,這樣就節省了不少 http 請求,加快了頁面切換的速度。但也帶來了一個明顯的缺點就是第一次加載應用的時候就要獲取到全部的頁面,致使首屏加載時間較長。幸運的是,服務端渲染(SSR)技術可以很好的解決這個問題。html

綜合來看,對於中小規模的應用,在現在強調先後端分離的時代,單頁應用是很好的選擇。接下來的分享是我在利用 vuevue-routervuex 開發單頁應用中遇到的一些問題以及對應的解決方法。前端

單頁應用的訪問權限

單頁應用不須要爲每一個頁面都新建一個 html 文件和若干個 cssjs文件,頁面切換不須要後端的控制,看起來貌似對前端開發人員很友好,但實際狀況是,對於大多數單頁應用而言,前端開發人員須要考慮的東西更多了。主要在於頁面訪問權限的控制,例如在哈希模式下,用戶能夠手動改變url的哈希值來訪問不一樣的路由,但在實際應用中,對於用戶的這種操做是須要進行判斷控制的,例如,某些路由須要登陸以後纔有權限訪問、某些路由對應的頁面須要特殊的權限才能訪問等。vue

我將單頁應用中的頁面類型分爲如下兩種:web

  • 登陸頁面
  • 須要登陸以後才能訪問的頁面

登陸頁面的權限控制

登陸頁面的特殊之處在於,登陸頁面只應該在用戶須要進行登陸操做的時候纔對其可見,也就是說,若用戶的登陸狀態沒有過時,則用戶不能訪問登陸頁面的路由。ajax

須要登陸以後才能訪問的頁面的權限控制

vue 應用中,一般會在 mounted 方法中發送 ajax 請求獲取數據。但在單頁應用中,用戶能夠改變 url 中的哈希值來訪問不一樣的路由,但對於某些頁面,例如我的中心等,須要用戶登陸以後才能獲取到有效的數據進行展現。雖然有一種簡單的處理方法:容許用戶訪問這個路由的頁面,但 ajax 獲取不到有效數據,但顯然不是最好的解決辦法。比較好的作法是,經過 vue-router生命週期函數判斷進入這些路由前,用戶是否已經登陸,沒有登陸則重定向到登陸頁面的路由。vue-router

router.beforeEach((to, from, next) => {
    if (to.path == '/login') { // 訪問登陸頁面的路由
        if (login) { // 登陸狀態有效,重定向到首頁
            next('/');
        } else { // 須要登陸
            next();
        }
    } else { // 訪問其餘的路由
        if (login) { // 登陸狀態有效,容許訪問
            next();
        } else { // 須要登陸,重定向到登陸頁面
            next('/login');
        }
    }
})

登陸狀態

通過上面的分析,問題最終都指向了 前端如何判斷用戶的登陸狀態vuex

一般狀況下,前端只負責發送請求,根據響應顯示頁面的不一樣內容,然後端會經過 sessionId 或者 token 來判斷用戶的登陸狀態是否過時。那麼前端有哪些辦法(哪些地方) 記錄(保存)用戶的登陸狀態是否有效這個數據呢?後端

客戶端存儲

sessionStoragelocalStoragecookie 是前端經常使用的客戶端存儲方法。瀏覽器

cookie

做爲讓後端有記憶能力的解決辦法,一般會攜帶一些後端判斷請求是來着於誰的信息,如 sessionId / token,如前面所說,這些信息仍是由後端判斷是否過時,通常不會在 cookie 中直接存儲登陸狀態是否有效。

sessionStorage

存儲在瀏覽器中,能夠在用戶登陸成功以後利用 setItem 保存一個屬性,在路由的生命週期函數 beforeEach 中經過 getItem 判斷是否已經登陸。實現簡單,但存在的問題較多:

  • sessionStorage 自己的過時時間決定了用戶的登陸狀態與 sessionStorage 的有效期同步。例如關閉瀏覽器以後都得從新登陸,而無論後端是否設置了 cookieexpire 屬性。

    • 後端的登陸狀態過時了,但 sessionStorage 還有效,致使用戶本能訪問頁面,只是沒有數據。用戶只能經過一些操做讓 sessionStorage 失效(關閉瀏覽器窗口從新進)
    • 後端的登陸狀態有效,但 sessionStorage 沒有了,致使用戶須要從新登陸。
  • sessionStorage 是可讀可寫的,在瀏覽器的調試工具中能夠輕鬆執行 clear 等方法,也能輕鬆讀取到 setItem 的值,低安全性也決定了 sessionStorage 不能存儲重要信息。

localStorage

除了過時時間與 sessionStorage 不同,其餘的功能都相似。

應用存儲

在應用中存儲,一般就是在 vuex 中記錄用戶是否登陸。在路由的生命週期函數 beforeEach 中讀取這個數據來判斷用戶的登陸狀態是否有效。那麼問題來了:

  • 登陸成功以後更新這個數據沒問題,還須要何時更新這個數據呢?不更新就一直處於登陸有效的狀態
  • 直接刷新頁面以後 vuex 清空了,若後端的登陸狀態是有效的,也要從新登陸嗎?

個人解決辦法是:經過發送一個請求判斷登陸狀態是否有效。對於獲取數據的請求,若返回值中的信息表明須要從新登陸了,則清空 vuex 並重定向到登陸頁面

來看看利用 vuex 記錄登陸狀態時會遇到的全部狀況以及處理辦法:

router.beforeEach(async (to, from, next) => {
    if (to.path == '/login') { // 訪問登陸頁面的路由
        if (store.login) {
            // 登陸狀態有效,重定向到首頁
            // 這種狀況一般是用戶手動修改了 url 中的 hash
            next('/');
        } else {
            // 發送 ajax 判斷登陸狀態是否有效
            const login = await ajax();
            if (login) {
                // 修改 store,重定向到首頁
                store.commit('login', true);
                next('/');
            } else {
                next();    
            }
        }
    } else { // 訪問其餘的路由
        if (store.login) {
            // 登陸狀態有效,容許訪問
            next();
        } else {
            // 尚未通過登陸頁面就訪問其餘路由,這種狀況一般也是用戶手動修改了 url 中的 hash
            // 發送 ajax 判斷登陸狀態是否有效
            const login = await ajax();
            if (login) {
                // 修改 store,容許訪問
                store.commit('login', true);
                next();
            } else {
                next('/login');    
            }
        }
    }
})

另外,對於每個獲取數據的請求,若後端的返回值中表明用戶的登陸狀態已通過期,則改變 vuex 中的登陸狀態並重定向到登陸頁:

store.commit('login', false);
location.href = '/#/login';
location.reload();
相關文章
相關標籤/搜索