iT邦幫忙

2026 iThome 鐵人賽

DAY 12
0
JavaScript

從看不懂到做出來,用 PawPal 走過前端新手村系列 第 12

Day 12|為什麼登入後還要知道「我是誰」?

  • 分享至 

  • xImage
  •  

今天的故事

上一篇做到註冊後自動登入,使用者已經可以直接進入會員頁面。

但接著我開始遇到另一個問題:

使用者雖然已經登入了,前端到底是怎麼知道現在登入的是誰?

當時我對會員流程的理解還很簡單。

我直覺認為,既然登入成功後已經拿到 Token,那前端應該就是靠 Token 去判斷目前的使用者。

直到開始處理個人資料與 Dashboard 的會員資訊時,我才慢慢發現,這件事其實比我原本想的多了一層。

在 PawPal 裡,登入成功時,後端其實不只回傳 Token,也會一起回傳基本的會員資料。

前端會把這些資料存進 auth store,畫面需要顯示會員姓名時,就可以直接從目前保存的 user 資料中取得。

但另一方面,當前端要去取得寵物資料或其他屬於目前會員的受保護資源時,又不是只靠畫面上保存的 user。

這時候還是需要帶著 Token 發出請求,讓後端驗證這次請求屬於哪一位登入者。

這也是我第一次開始理解:

「前端現在顯示的是誰」和「後端怎麼確認這次請求是誰送的」,其實是兩件相關、但不同的事情。


登入成功後,前端其實已經有基本會員資料

我一開始最大的誤解,是把登入想成:

登入成功
→ 取得 Token
→ 再透過 Token 取得會員資料

但 PawPal 實際上的登入流程不是只有這樣。

登入成功後,後端會回傳類似這樣的資訊:

token
+
user

其中 user 會包含像是:

  • id
  • name
  • email
  • avatar_url
  • created_at

前端收到資料後,會把 Token 和 user 都保存到 auth store,並同步保存到 localStorage。

例如:

pawpal_token
pawpal_user

所以登入完成後,前端其實已經擁有一份可以直接拿來顯示的基本會員資料。

像 Dashboard 上的會員名稱,就是直接從:

authStore.user

取得。

這和我原本想像的:

登入後前端完全不知道是誰,所以一定要再呼叫一次 Current User API。

其實不太一樣。

PawPal 的登入回應本身,就已經先把基本的 user 資料交給前端了。


那 Current User API 又是做什麼?

後來查回 Repository,我才確認 PawPal 後端其實真的有一支 Current User API:

GET /api/v1/users/me

它的用途是:

前端帶著 Bearer Token 發出請求
→ 後端驗證 Token
→ 從 JWT payload 取得目前使用者 id
→ middleware 將它放進 req.userId
→ 依照 req.userId 查詢會員資料
→ 回傳目前登入者的 user 資料

簡單來說,這支 API 解決的是:

後端如何根據這次請求的登入身分,找出目前這位會員的資料?

但這裡有一個我後來才釐清的地方。

雖然 /users/me 真的存在,PawPal 當時登入完成後,前端並不是每次都立刻再呼叫這支 API 取得會員資料。

因為登入 API 本身就已經回傳 user,前端也已經把它存進 auth store。

所以更接近實際情況的理解應該是:

登入成功
→ Login API 回傳 token + user
→ authStore 保存 token、user
→ 畫面直接使用 authStore.user

而當系統需要取得目前會員資料時,後端也提供 /users/me 這類受保護 API 來處理。


Token 負責的是「證明這次請求是誰」

這時候我才慢慢把 Token 和 user 資料分開來看。

登入回傳的 user 比較像:

前端目前可以直接使用的會員基本資料。

而 Token 的角色則比較像:

當前端之後再向後端要求受保護資料時,用來證明這次請求代表哪一位登入者。

在 PawPal 裡,受保護的 Request 會帶上:

Authorization: Bearer <token>

後端的 authenticateToken middleware 驗證 Token 後,會從 JWT payload 的 sub 取得使用者 id,再放進:

req.userId

後面的 controller 或 service 就可以透過 req.userId 取得目前登入者的身分,並查詢屬於這位會員的資料。

這個觀念也讓我開始理解,Token 並不是專門拿來讓畫面顯示姓名或 Email。

它更重要的角色,是讓後端在後續 Request 中確認登入身分。


Dashboard 其實也分成兩種資料來源

我原本把 Dashboard 想得太簡單。

當時我會覺得:

先知道目前使用者是誰
→ 再取得 Dashboard 的所有資料

但實際上,PawPal 裡不同資料的來源並不完全一樣。

例如 Dashboard 顯示會員姓名時,可以直接讀:

authStore.user.name

因為登入時 user 已經被保存下來了。

但像寵物資料這類「屬於目前會員的資料」,就不是先呼叫 Current User API,再用那份結果去取得寵物。

而是:

前端帶著 Bearer Token 呼叫寵物 API
→ authenticateToken 驗證 Token
→ 從 payload.sub 取得使用者 id
→ middleware 將 id 放進 req.userId
→ 後端依 req.userId 查詢這位會員的寵物
→ 回傳結果

所以我後來才發現:

「取得目前會員資料」和「取得屬於目前會員的資源」其實也不是完全相同的流程。

前者關心的是會員本人資料。

後者則是利用已驗證的登入身分,去限制這次 Request 能取得哪些資料。


我一開始還是先從畫面看問題

當時處理個人資料功能時,我最先注意的還是畫面。

我希望先把個人資料介面做出來,再確認 API 有沒有成功回傳資料,最後把登入者的姓名和 Email 顯示在畫面上。

流程看起來很直覺:

先完成畫面
→ 呼叫 API
→ 取得資料
→ 綁定到畫面

但實際做下去後,我才發現:

畫面做出來,不代表資料就會自動出現。

還需要確認:

  • Request 是否真的送出?
  • Token 是否有一起帶上?
  • Response 實際回傳什麼?
  • auth store 裡現在保存的是什麼?
  • 元件最後從哪裡取得資料?

只要其中一層沒有對上,畫面就可能沒有出現預期內容。


API 有成功,不代表前端就一定拿對資料

當個人資料沒有正常顯示時,我第一個懷疑的就是 API。

後來我開始一步一步檢查。

我先確認前端有沒有發出 Request,再去看後端 controller 實際回傳的 Response。

我記得當時讓我印象很深的是,問題和 Response 的資料層級有關。

後端其實有回傳資料,但前端實際讀取的位置,沒有和 Response 結構完全對上。

這段是我對當時 Debug 過程的記憶,目前已經找不到 Repository 可以完整還原那次錯誤的證據,所以我把它當成自己的開發經驗來保留。

但這次讓我學到的觀念本身沒有變:

API 成功回應,不代表前端一定取到自己以為的那一層資料。

例如以下只是簡化概念,不是 PawPal 當時的實際 Response:

{
  "user": {
    "name": "R-HAO",
    "email": "example@example.com"
  }
}

如果前端直接讀:

response.data.name

就拿不到 name

因為 name 實際上是在:

response.data.user.name

這次我才真正感受到:

前後端不只要確認欄位名稱,連資料被包在哪一層也要看清楚。


我開始去看後端 controller

以前遇到前端沒有資料時,我可能只會一直看畫面:

  • 是不是變數名稱錯了?
  • Vue 綁定是不是沒有成功?
  • 畫面是不是沒有重新渲染?

但這次我開始往後端看。

我去對照 controller 實際回傳的 Response,確認後端到底把資料包成什麼格式。

對現在來說,這可能只是很基本的 Debug 動作。

但對當時的我來說,是一個很重要的改變。

因為我不再只問:

API 有沒有成功?

而是開始問:

API 成功後,到底回傳了什麼?

看到 200 或其他 2xx 成功狀態,只能先知道這次 Request 在 HTTP 層級得到了成功的回應。

前端還是要繼續確認:

  • Response 的內容是不是預期資料?
  • 資料放在哪一層?
  • API 模組最後回傳哪一層?
  • 畫面實際讀取的又是哪一層?

不只欄位名稱,資料流也要一起看

前面處理寵物資料時,我已經知道前後端欄位名稱要對得上。

這次我又多理解了一件事:

只看欄位名稱還不夠,還要看資料一路經過哪些地方。

以 PawPal 會員資料來說,至少會遇到兩種不同情境。

登入時:

Login API
→ 回傳 token + user
→ authStore 保存
→ UI 讀取 authStore.user

需要重新向後端取得目前會員資料時:

Bearer Token
→ GET /api/v1/users/me
→ authenticateToken
→ req.userId
→ 查詢會員資料
→ 回傳 { user }

而其他會員專屬資料,例如寵物資料,又會走:

Bearer Token
→ 受保護 API
→ authenticateToken
→ req.userId
→ 查詢屬於這位會員的資料

這也是我後來才真正看懂的地方。

「我是誰」這件事,在前端畫面、會員資料 API,以及其他受保護資源裡,其實會以不同方式出現。


登入 API 和 Current User API 有什麼不同?

整理到這裡後,我才比較能分清楚這兩支 API 的角色。

Login API 是登入入口,負責確認帳號密碼,成功後回傳:

token + user

Current User API 則是在已經有 Token 的情況下,讓後端依照登入身分查出目前會員資料。

所以兩者最大的差別,不是「哪一支比較重要」,而是它們處理的是會員流程中的不同階段。

一支負責登入。

另一支負責在登入後,依照身分取得目前會員資料。

這也是我以前很容易混在一起的地方。


如果現在重新做一次

如果現在重新遇到畫面沒有資料的問題,我不會只看到 API 成功,就直接假設資料沒有問題。

我會依序確認:

Request 是否真的送出
→ Authorization / Token 是否正確
→ HTTP 狀態是否符合預期
→ Response 完整結構是什麼
→ API 模組最後回傳哪一層
→ auth store 或其他 state 保存什麼
→ 元件實際從哪裡讀取

如果是受保護 API,我也會一起往後端確認:

authenticateToken 是否成功
→ req.userId 是否正確
→ Controller / Service 實際查了哪些資料

這樣就不會只停在:

API 有回 200,所以應該沒問題吧?

而是沿著完整資料流,一層一層確認。


本篇重點與學習心得

這次可以先記住幾件事:

  1. PawPal 登入成功後,不只會拿到 Token,也會拿到基本的 user 資料。
  2. 前端可以從 auth store 直接取得登入會員的基本資訊。
  3. Token 主要負責讓後端在後續 Request 中驗證登入身分。
  4. PawPal 也有 GET /api/v1/users/me 可以依照登入身分取得目前會員資料。
  5. Dashboard 顯示會員姓名,和取得會員專屬的寵物資料,其實是不同資料流。
  6. API 成功回應,不代表前端一定讀到了正確的 Response 層級。
  7. 遇到資料沒有顯示時,不只要看狀態碼,也要把整條資料流看清楚。

這次最讓我有感的,不是哪一支 API 要怎麼寫。

而是我開始知道:

系統裡的「我是誰」,不是只有一個地方在處理。

前端需要知道現在畫面要顯示誰。

後端需要知道這次 Request 是誰送的。

而不同的受保護功能,又需要依照這個登入身分限制可以取得的資料。

也是從這次開始,我不再只看畫面有沒有資料,而是開始沿著:

前端
→ API
→ middleware
→ controller / service
→ Database

一路去找答案。


下一篇預告

知道 PawPal 怎麼保存會員資料,以及後端怎麼透過 Token 辨認登入者之後,下一個問題就是:

Token 到底是什麼?後端又是怎麼驗證它的?

當時我原本以為自己已經懂 JWT。

直到面試真的被問到,我才發現很多地方其實只是「用過」,還沒有真正理解。

下一篇會接著整理 JWT 的結構、Bearer Token、middleware,以及後端如何驗證登入身分。

下一篇:

Day 13|面試被問到 JWT,我才發現自己沒有真的懂


上一篇
Day 11|註冊成功後要去哪裡?從註冊頁開始理解會員流程
系列文
從看不懂到做出來,用 PawPal 走過前端新手村12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言