MODULE 6 · 登入與驗證 · 6-15

登入了,然後呢?誰擋得住直接打 /dashboard 的人?

上一課結束時,你的 App 已經認得你是誰了(Authentication)。但這只解決了一半。使用者只要在網址列直接敲 your-app.com/dashboard,就能繞過你精心做的登入按鈕,瀏覽器不會幫你問「這個人登入了嗎」。這一課要請一位 網站保全 站在每一個請求的門口:登入的人放行,沒登入的人請回登入頁。這就是 Authorization 的邊界:把「你能進哪裡」這條線畫出來。

本課地圖(6-15 保護路由)
01沒有保全的網站:直接打網址就進得去
02Middleware:請求門口的網站保全
03保全在檢查什麼:你的 Session
04放行,還是導回登入頁
05哪些頁要擋:路由保護規則
06把這條界線講給 AI 聽
01 · 這是什麼

沒有保全的網站:所有頁面全世界可見

先看一個 AI demo 最常見的洞:登入按鈕做得漂亮,但 /dashboard 直接打網址就進得去。你自己來闖一次。

在網址列直接打:
your-app.com/
💻
歡迎使用你的 App這是公開首頁,本來就該人人能看
要登入才能看
Protected Routes 受保護頁面
只該給登入者看的頁,例如 /dashboard/settings、後台。被直接打開時必須先驗身分。
人人可看
Public Routes 公開頁面
本來就該人人能看的頁,例如首頁 /、定價頁 /pricing、還有登入頁 /login 自己。這些不該擋。
🔑VIBE CODER 秘訣
👀觀察
AI 幫你做的 demo,登入按鈕、登入後跳轉都做得很漂亮,看起來「有 auth 了」。但它常常只做了 Authentication(你是誰),沒做 Authorization(你能進哪)。結果 /dashboard/admin 直接打網址就進得去。本機自己玩看不出來,因為你一直是登入狀態。
💬怎麼跟 AI 講
別只說「加上登入」。把邊界講出來:「這個 App 要上線,請加上保護路由:未登入的人不能進 /dashboard 和 /settings,會被導回登入頁;首頁和定價頁保持公開。」把「哪些要擋、未登入導去哪」說清楚,AI 才會真的立起保全。
02 · 這是什麼

Middleware:站在每個請求門口的保全

在請求碰到頁面之前,先攔下來檢查一次。框架叫它 middleware,心智模型就是「保全」。

瀏覽器的一次敲門
Request 請求
每次有人想看你的頁面,瀏覽器就送出一個請求。它帶著你是誰的線索(Session)一起走到門口。
門口的保全
Middleware
在請求真正碰到頁面之前先攔下來跑一次的程式。重點是位置:站在門口,不是房間裡。各框架名字不同,位置一樣。
請求管線
🧑‍💻使用者
🛡️保全
📄/dashboard
req

保全上班:送出請求看看,它會在保全處被攔下。

關鍵在位置:保全站在頁面被渲染之前。所以擋下未登入者的時候,私密資料根本還沒被讀出來,更別說傳給對方。這跟「頁面自己跳出一個『請先登入』的彈窗」完全不同,那時候資料早就送到瀏覽器了。

03 · 怎麼運作

保全到底在檢查什麼:Session / Token Check

保全不是看你長相,是看你手上那張 6-14 拿到的 Session 還有效嗎。

保全要看的那張證
Session / access token
6-14 登入後瀏覽器拿到的那張證。每個請求都帶著它走到門口,保全要檢查的就是它。下面三項全過才放行。
有沒有
這張證在不在
是不是真的
簽章是 Firebase 發的,沒被偽造
過期沒
exp 時間還沒到
🛡️保全檢查台PASS
有帶 Session(access token)
簽章是 Firebase 發的,沒被偽造
還沒過期(exp 時間未到)
📝想一下
你的 App 有漂亮的「Continue with Google」登入按鈕,登入後會跳轉到 /dashboard。一個從沒登入的人,直接在網址列打 your-app.com/dashboard,會發生什麼?
04 · 怎麼運作

兩條路:放行,還是 Redirect to Login

同一個請求,檢查通過直接進頁面,檢查失敗就被導回登入頁。挑個身分,自己走兩遍。

用誰的身分打 /dashboard:
your-app.com/dashboard
💻
選好身分,按下方「打 /dashboard」
準備好了就出發。同一道保全,差別只在你手上那張 Session。

同一個請求、同一道保全,差別只在手上那張 Session。檢查通過放行,失敗就導回登入頁,這條「能進 / 不能進」的界線就是 Auth Boundary(授權邊界)

05 · 怎麼運作(規則面)

路由保護規則:哪些頁要擋

不是每頁都要擋。哪些公開、哪些要登入,是你給保全的一張名單。自己分類看看。

預設全開
黑名單策略
預設每頁都公開,只擋你列出來的。漏標一頁,它就「不小心全世界可見」(資料外洩)。
預設全擋
白名單策略
預設每頁都要登入,只放行你列出來的公開頁。漏標一頁,它只是「不小心被擋住」(使用者抱怨進不去)。更安全。
要錯,也要錯在安全的那一邊。所以實務上用白名單當預設。
把每個路徑分類:Public(公開)或 Protected(要登入)
/
首頁
/pricing
定價頁
/login
登入頁
/dashboard
營運後台
/settings
個人設定
/api/orders
訂單 API
別漏了 API保護不是只擋畫面頁。/api/orders 這種 API route 也要保全,否則有人不開畫面、直接打那個網址,一樣把訂單資料整包撈走。保全要同時站在「畫面門口」和「資料門口」。
進階補充:保全怎麼知道一個網址該不該擋(matcher)

你不會一條一條手寫「/dashboard 要擋、/dashboard/orders 也要擋」。框架讓你寫一條樣式(pattern)一次涵蓋一整區,例如「/dashboard 開頭的全部都算受保護」。

export const config = {
  matcher: ['/dashboard/:path*', '/settings/:path*', '/api/:path*'],
}

:path* 是「後面接什麼都算」。一行就把整個 /dashboard 底下、整個 /api 底下全納進保全的管轄。名字和語法各框架不同,概念一樣:用樣式描述「哪一區要過保全」。

📝想一下
你新增了一個 /admin 後台頁,但忘了把它加進保護名單。哪一種預設策略能讓這個疏忽「比較不致命」?
06 · 對你的意義

把界線講給 AI 聽:Auth Boundary 怎麼開需求

AI 很會做登入按鈕,常常忘記做保全。這一節把整課收成你跟 AI 開需求要交代的幾件事。

你以為已經安全
  • 做了漂亮的 Google 登入按鈕
  • 登入後會跳轉到 /dashboard
  • 自己測一切正常(因為你一直登入著)
實際還要做的
  • 哪些頁公開、哪些要登入
  • 未登入打受保護頁,導回 login 並帶回原頁
  • API route 也要檢查 Session
  • 用「預設要登入」的白名單策略兜底
🔑VIBE CODER 秘訣
👀觀察
就算 AI 做了頁面層的保護,常常漏掉 API。畫面 /dashboard 擋住了,但 /api/orders 還是裸奔,有人直接打那個網址一樣把資料撈走。保全只站在「畫面門口」,沒站在「資料門口」。
💬怎麼跟 AI 講
把保護範圍講全:「保護不只做在頁面,API route 也要檢查 Session;另外請用『預設需要登入、只放行我列出的公開頁』的策略,這樣我之後新增頁面忘了標記,也是預設被擋住而不是外洩。」要求 AI 連 API 和預設策略一起處理,洞才補得乾淨。
收尾

這一段你帶走了什麼

登入按鈕只解決「你是誰」,網址列可以繞過它。要靠保全(middleware)站在請求門口檢查。
保全檢查的是 6-14 拿到的 Session:有沒有、是不是真的、過期了沒。
檢查通過放行,失敗就 Redirect to Login,最好還帶使用者回原本想去的頁。
先分清 Public Routes 與 Protected Routes,用「預設要登入」的白名單策略最安全。
保護要做全:頁面和 API 都要擋,這條界線叫 Auth Boundary,是你要講清楚給 AI 的需求。
6-16 預告:關聯用戶資料

現在沒登入的人進不來了,門口的保全也站好了。但登入進來的人,看到的應該是他自己的資料,不是別人的。小明不該看到小華的訂單。下一課我們把使用者和他的資料關聯起來,讓每個人登入後只看得到屬於自己的那一份。