原文鏈接:https://github.com/ly525/blog...
關鍵字:http-only
, cookie
,sessionid
, vue-router
, react-router
, 安全
,localStorage
, jwt
需求描述
- 內部管理平臺,需要用戶登錄之后才能訪問。現在將 該平臺地址(www.xxx.com/home) 直接發給新來的運營同學
- 前端需要檢測該用戶是否已登錄,如果未登錄,則將其 redirect 到登錄頁面
- 因為該頁面為單頁應用,路由跳轉不涉及后端的 302 跳轉,使用前端路由跳轉
實現思路
實現代碼
// 以 vue-router 為例
// 登錄中間驗證,頁面需要登錄而沒有登錄的情況直接跳轉登錄
router.beforeEach((to, from, next) => {const hasToken = document.cookie.includes('sessionid');// 如果采用 jwt,則同樣 hasToken = localStorage.jwtconst pathNeedAuth = to.matched.some(record => record.meta.requiresAuth);// 用戶本地沒有后端返回的 cookie 數據 && 前往的頁面需要權限// if (pathNeedAuth && !hasToken ) {next({path: '/login',query: { redirect: to.fullPath },});} else if (hasToken && to.name === 'login') {// 已登錄 && 前往登錄頁面, 則不被允許,會重定向到首頁next({path: '/',});} else {next();}
});
-
應該在進入任何頁面之前,判斷:
- 該頁面是否需要權限才能訪問:登錄、注冊頁面不需要權限
-
用戶是否已經登錄:本地 cookie (或者 localStorage)包含 session 相關信息
Cookie: csrftoken=YaHb...; sessionid=v40ld3x....
-
如果
A頁面需要權限
且本地 cookie中包含了 sessionid 字段
,則允許訪問A頁面,否則跳轉到登錄頁面- 備注:sessionid 該字段由用戶在登錄之后,由后端框架通過
response.setCookie
寫入前端 ,因此該字段需要和后端同學確認 - 需要后端同學在 response header 中配置cookie中該字段的 http-only屬性,允許前端讀取 cookie。否則前端通過 document.cookie 讀取到的 cookie 將不包含 sessionid 字段
- 這個時候,可能會存在 js 讀取cookie 導致不安全的情況,后端同學可以把 cookie 中的某個字段設置為 允許讀取,其他 cookie 設置不允許讀取,這樣即使被第三方不安全腳本獲取,也無法產生負面影響。
- 備注:sessionid 該字段由用戶在登錄之后,由后端框架通過
- 如果
用戶已登錄
&& 在瀏覽器中輸入了登錄頁
地址,則將其重定向到首頁
更多說明
- 這樣做,前端就不必再向后端發起 API 做權限鑒定了(后端返回401,前端跳轉到 401),減少不必要的API 請求,特別是如果在API響應時間過長的情況下,體驗不太友好。
-
用戶修改 cookie,偽造 sessionid
- 這樣的話,前端就無能為力了,前端鑒權此時認為該用戶合法。此時訪問首頁,將會調用獲取數據的API。
- 瀏覽器會將
用戶偽造的 sessionid
帶給后端,這時候就需要后端對 sessionid 進行較驗了。比如校驗前端帶來的 sessionid 與數據庫中的 sessionid 是否一致 - 用戶偽造的數據 sessionid 和 后端數據庫中 sessionid 的概率 非常非常低,可以忽略不計,因為 sessionid 的位數一般在 32 位以上,因為里面包含了字母和數字,也就由 32 ^ 36 種可能
- 結論:偽造沒有意義,即使用戶可以看到頁面的樣子,但是看不到數據
-
采用 localStorage 而不是 sessionStorage 的原因(SessionStorage 失效場景)
原因:sessionStorage 無法跨 Tab 共享
- 用戶在新打開一個 Tab,輸入已經登錄之后的某個頁面
- 通過
target="_blank"
來打開新頁面的時候,會導致會話失效 - 在當前頁面執行
Duplicate
(復制 Tab),sessionStorage 失效