跳至主要内容

4 篇文章 含有標籤「JWT」

檢視所有標籤

JWT 使用好處入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

前言

JWT(JSON Web Token) 的好處在於它是輕量、跨平台、無狀態的驗證與授權機制,特別適合用於前後端分離、微服務架構與 API 驗證。以下是它的主要優點:

一、JWT 的 7 大好處

1. 無狀態(Stateless)驗證

  • JWT 本身就包含用戶資訊與簽名,不需要在伺服器端儲存 session
  • 適合微服務或 Serverless 架構,因為伺服器不用記住誰是誰。

2. 跨平台、跨語言支援

  • JWT 是一種標準格式(RFC 7519),可用於各種語言與框架(Node.js、Python、Go、Java、PHP、.NET 等)。
  • 非常適合前後端分離的架構,或多平台應用(Web + App)。

3. 傳遞資訊方便

  • 可以在 payload 中攜帶使用者角色、權限、使用者 ID 等資料,前後端都可解碼取得。
  • 節省額外查詢資料庫的頻率(如登入後不再查資料庫即可知道使用者身份)。

4. 易於擴展與整合 OAuth

  • JWT 是 OAuth 2.0 中 Access Token 的常見格式。
  • 整合第三方登入(如 Google、Facebook、GitHub)時很常使用 JWT 作為驗證令牌。

5. 高安全性(搭配正確實作)

  • Token 是簽名過的,無法被竄改(除非持有密鑰)。
  • 可設定有效期限(exp),也支援附加自訂欄位。

6. 前端儲存與攜帶方便

  • JWT 是字串格式,可透過:

    • Authorization header:Bearer <token>
    • Cookie(推薦設為 HttpOnly + Secure)
    • localStorage(需防範 XSS)

7. 支援單一登入(SSO)

  • 多個子系統共用同一套 JWT 驗證邏輯,實現單一登入(Single Sign-On)。
  • 用於企業內部系統、微服務架構特別合適。

二、實際應用情境

應用場景優勢
API 驗證無狀態、易於整合
單頁應用(SPA)可存取使用者資訊而不額外請求
手機 App 登入移動端儲存與驗證方便
微服務架構每個服務只需驗證簽章,無需共用 session
OAuth 第三方登入可當作 Access Token 與 ID Token

三、與 Session 的比較

項目JWTSession
資料儲存位置前端(Token 傳遞)後端(記憶體/資料庫)
可擴充性高(無需共享記憶體)需維護集中式 Session 儲存
實作難度中(需處理 Token 過期、黑名單等)簡單(有框架支援)
安全性好(但需避免 XSS 與 Token 洩露)好(但可能遭 CSRF 攻擊)

四、常見誤解釐清

常見誤解釐清
JWT 是加密的錯,預設只是簽名(不可竄改),不是加密(不可閱讀)
JWT 安全無敵錯,若密鑰外洩,任何人都能簽 Token。需配合 HTTPS 傳輸與妥善保護密鑰
JWT 不需要過期時間錯,若未設 exp,Token 可永久有效,極度危險

五、總結

JWT 的優勢在於簡潔、無狀態、可擴展,特別適合 API 驗證與前後端分離架構。然而,要發揮 JWT 的最大效益並保障安全,開發者需了解其限制,搭配正確實作(如過期、黑名單、HTTPS、Refresh Token 管理等)。

React 前端整合 JWT(含 Refresh Token)入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

當你要將 JWT 驗證整合至 React 前端,並搭配 Refresh Token 或整合 OAuth(如 Google 登入)流程,你需要考慮前端的存儲方式、token 的更新機制,以及第三方登入的流程銜接。

以下將分成三個部分講解:


一、React 前端整合 JWT(含 Refresh Token)

流程總覽:

  1. 使用者輸入帳密登入,發送 /login 請求。

  2. 伺服器簽發兩種 token:

    • Access Token(短效,有效期如 15 分鐘)
    • Refresh Token(長效,有效期如 7 天)
  3. 前端儲存 Access Token(如在記憶體),Refresh Token 建議儲存在 HttpOnly Cookie

  4. 當 Access Token 過期時,自動用 Refresh Token 換取新 Access Token。

  5. 使用者主動登出時,Refresh Token 一併清除。


伺服器回傳範例(登入成功):

{
"accessToken": "xxxxx",
"expiresIn": 900
}

並透過 Set-Cookie 傳送 HttpOnly 的 Refresh Token:

Set-Cookie: refreshToken=xxxxx; HttpOnly; Path=/; Max-Age=604800;

React 實作要點

// login.js
import axios from 'axios';

export async function login(username, password) {
const res = await axios.post(
'/api/login',
{ username, password },
{
withCredentials: true, // 允許 cookie 傳遞
}
);

localStorage.setItem('accessToken', res.data.accessToken);
}

// api.js
import axios from 'axios';

const api = axios.create({
baseURL: '/api',
withCredentials: true,
});

api.interceptors.request.use((config) => {
const token = localStorage.getItem('accessToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});

api.interceptors.response.use(
(res) => res,
async (err) => {
if (err.response.status === 401) {
try {
// 嘗試使用 Refresh Token 換 Access Token
const res = await axios.post('/api/refresh', {}, { withCredentials: true });
localStorage.setItem('accessToken', res.data.accessToken);

// 重試原本請求
err.config.headers.Authorization = `Bearer ${res.data.accessToken}`;
return axios(err.config);
} catch (e) {
// refresh 失敗,跳轉登入頁
window.location.href = '/login';
return Promise.reject(e);
}
}
return Promise.reject(err);
}
);

export default api;

登出

export async function logout() {
await axios.post('/api/logout', {}, { withCredentials: true });
localStorage.removeItem('accessToken');
}

備註

  • Refresh Token 儲存在瀏覽器的 HttpOnly Cookie,無法被 JavaScript 存取,提升安全性。
  • Access Token 儲存在記憶體或 localStorage(但 localStorage 易受 XSS 攻擊)。
  • 若要完全防止 CSRF,Refresh Token cookie 需搭配 SameSite 設定與 CSRF token。

二、OAuth 2.0 登入(以 Google 為例)

流程總覽

  1. 前端點擊「使用 Google 登入」。
  2. 透過 Google OAuth 流程取得授權碼(或 id_token)。
  3. 前端將該 token 傳送到後端 /auth/google
  4. 後端驗證 Google id_token,並簽發 JWT 給前端。

React 前端整合(Google 登入)

使用 @react-oauth/google

npm install @react-oauth/google
import { GoogleOAuthProvider, GoogleLogin } from '@react-oauth/google';

function App() {
return (
<GoogleOAuthProvider clientId="你的 Google Client ID">
<GoogleLogin
onSuccess={(credentialResponse) => {
// 將 id_token 傳送給後端
fetch('/api/auth/google', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ token: credentialResponse.credential }),
});
}}
onError={() => {
console.log('登入失敗');
}}
/>
</GoogleOAuthProvider>
);
}

後端處理(Node.js + google-auth-library

import { OAuth2Client } from 'google-auth-library';
import jwt from 'jsonwebtoken';

const client = new OAuth2Client(GOOGLE_CLIENT_ID);

router.post('/auth/google', async (req, res) => {
const { token } = req.body;

const ticket = await client.verifyIdToken({
idToken: token,
audience: GOOGLE_CLIENT_ID,
});

const payload = ticket.getPayload();

const jwtToken = jwt.sign({ userId: payload.sub, name: payload.name }, process.env.JWT_SECRET, {
expiresIn: '15m',
});

const refreshToken = jwt.sign({ userId: payload.sub }, process.env.JWT_REFRESH_SECRET, {
expiresIn: '7d',
});

res
.cookie('refreshToken', refreshToken, {
httpOnly: true,
maxAge: 7 * 24 * 60 * 60 * 1000,
})
.json({ accessToken: jwtToken });
});

三、小結

功能JWT + RefreshOAuth 2.0
是否需要帳密登入否,透過第三方登入
Token 儲存Access Token: localStorage / memory
Refresh Token: HttpOnly Cookie
同上
適合對象自建會員系統使用 Google / Facebook / LINE 等快速登入
安全性良好,需搭配 HTTPS高,由 Google 等大廠管理
實作難度中等,需處理 Token 刷新邏輯中高,需處理外部驗證流程

延伸建議

  • 若你想使用前後端共用的 JWT 驗證邏輯,建議抽出 middleware 並集中處理。
  • 可搭配 jsonwebtokenaxios-auth-refresh 等工具。
  • 若前後端完全分離,建議使用跨網域 Cookie 搭配 SameSite=None; Secure

JWT 的實作安全策略與 Refresh Token 安全做法入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

一、JWT 基本安全策略

1. 使用強密鑰(Secret)或非對稱加密

  • 對稱加密:使用 HS256 時,secret 應具備高強度(建議 256-bit 以上)。
  • 非對稱加密:使用 RS256 時,私鑰與公鑰分離,便於服務間驗證。

2. 設定有效期限(exp)

  • Access Token 建議設為 短效(10~30 分鐘)
  • 避免永久有效的 Token,防止被盜用後長期濫用。

3. 限制 Token 權限與資訊

  • Token 中只放必要資訊(例如:userIdrole)。
  • 不要放敏感資料(如密碼、信用卡、住址等)。

4. 透過 HTTPS 傳遞 JWT

  • 絕對 禁止在 HTTP 傳輸 JWT,避免中間人攻擊(MITM)。
  • 所有 API 傳遞 token(包括 Refresh Token)都必須走 HTTPS。

5. 防範 XSS

  • 若將 accessToken 存放於 localStorage,需格外注意防止前端被注入腳本。
  • 建議使用 HttpOnly cookie 儲存 Refresh Token(無法被 JavaScript 存取)。

二、為什麼要使用 Refresh Token?

Access Token 有效期短,提高安全性,但使用者不希望頻繁重新登入,這時就用 Refresh Token 來「悄悄地」幫用戶更新 Access Token。


三、JWT + Refresh Token 實作架構

          +-------------+                      +----------------+
| 前端客戶端 | | 後端伺服器 |
+-------------+ +----------------+
| |
|--------> /login (帳密登入) --------->|
| |
|<----- accessToken + refreshToken -----|
| |
| 使用 accessToken 存取受保護資源 |
|--------> /api/profile ------------->|
| |
accessToken 過期 ↓ |
|--------> /refresh (更新 token) ------>|
|<------- 新的 accessToken -------------|

四、最佳實作策略詳解

1. accessToken 存放位置

  • 可存在記憶體(例如 Redux、React Context)或 localStorage
  • 優點:操作方便
  • 缺點:暴露於 XSS 攻擊風險

2. refreshToken 存放位置

  • 建議儲存在 HttpOnly、Secure 的 Cookie 中:
Set-Cookie: refreshToken=xxxxx; HttpOnly; Secure; SameSite=Strict; Path=/refresh
  • 前端 JavaScript 無法讀取,降低風險
  • 伺服器可以從 cookie 中自動取出 refreshToken

3. 建立 /refresh API(專責刷新 token)

// POST /refresh
import jwt from 'jsonwebtoken';

router.post('/refresh', (req, res) => {
const refreshToken = req.cookies.refreshToken;

if (!refreshToken) return res.status(401).json({ error: '無 refresh token' });

try {
const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);

const newAccessToken = jwt.sign(
{ userId: decoded.userId, username: decoded.username },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);

res.json({ accessToken: newAccessToken });
} catch (err) {
return res.status(403).json({ error: 'Refresh token 無效或過期' });
}
});

4. 可選:Refresh Token 儲存機制(實現強制登出與黑名單)

  • 資料庫方案(建議):

    • 每次登入時產生唯一的 refreshToken 並儲存於 DB
    • 使用時驗證是否仍存在
    • 登出或異常行為時移除對應的 refreshToken
  • Redis 快取黑名單方案(進階):

    • 遇到帳號盜用或封鎖時,將 token 加入黑名單清單,後續驗證可即時攔截

5. 登出處理(安全地移除 token)

router.post('/logout', (req, res) => {
res.clearCookie('refreshToken', { path: '/refresh' });
res.json({ message: '已成功登出' });
});

五、常見攻擊防護建議

威脅類型防護方法
XSS不使用 localStorage 儲存 refresh token,輸出內容時做 XSS 過濾
CSRF使用 SameSite=Strict 的 cookie,或搭配 CSRF Token
Token 竄改使用強加密簽章(如 HS256/RS256)並驗證
重放攻擊設定合理 exp 過期時間並結合裝置驗證(如 IP / UA)
Token 外洩refreshToken 實作多設備登入管理與黑名單機制

六、小結

元素實作策略優點
Access Token存放在記憶體或 localStorage快速存取,搭配短效時安全性高
Refresh Token存放於 HttpOnly Cookie避免 JS 存取,提高防護能力
/refresh API驗證 refreshToken 並產生新 accessToken提升使用者體驗與系統可用性
黑名單策略儲存 refreshToken 資訊於資料庫支援強制登出與封鎖帳號

七、額外建議

  • 單一使用者只能同時登入 N 台裝置,可依照 refreshToken 綁定設備資訊。
  • 將 JWT 實作與中介層(middleware)封裝好,避免開發重複邏輯。
  • 若你使用的是框架如 Next.js、NestJS、Spring Boot,也有內建 JWT 與 Refresh 模組可以善用。

參考文件

  1. 官方文件

JWT 入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

在現代 Web 應用中,使用者認證與授權機制是系統安全的重要基礎。傳統上,我們可能使用 Session 與 Cookie 搭配伺服器端儲存進行身份驗證;但在前後端分離、多平台(Web、Mobile、API Gateway)應用日益普及的情況下,更輕量、跨平台、無狀態的驗證方案逐漸成為主流。

JWT(JSON Web Token)正是這樣一種流行的解法。它是一種根據 JSON 格式加密後產生的 Token,可用來安全地在用戶與伺服器間傳遞資訊。


重點摘要

  • JWT 是一種開放標準(RFC 7519),用於在雙方之間以 JSON 格式安全傳遞資訊。

  • 結構由三部分組成:Header.Payload.Signature

  • 主要用途:

    • 身份認證(Authentication)
    • 授權(Authorization)
  • 特點:

    • 可跨語言與平台使用
    • 支援無狀態驗證(不需伺服器端儲存 session)
    • 可設定過期時間與自訂 Payload 欄位
  • 常見應用場景:API Token 驗證、行動裝置登入狀態維持、OAuth 搭配使用

  • JWT 須透過 HTTPS 傳遞以避免中間人攻擊(MITM)

  • 不適合儲存敏感資訊(如密碼、信用卡號)


JWT 結構說明

一個 JWT 字串長得像這樣:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJ1c2VySWQiOiIxMjM0IiwibmFtZSI6IktEIiwiZXhwIjoxNzAwMDAwMDAwfQ.
sKPXrY3AvKb0aBQKgYF3mn7ZWh9yGpyF2X2NFie5TIU

它由三個部分組成,透過 . 分隔:

  1. Header:定義演算法(如 HS256)與類型(JWT)。
  2. Payload:承載實際資料(如使用者 ID、帳號、過期時間)。
  3. Signature:用密鑰加密前兩部分,用於驗證是否被竄改。

實作範例(Node.js + ESM)

安裝必要套件

npm install express jsonwebtoken dotenv

專案架構

project/
├── index.js
├── routes/
│ └── auth.js
├── .env

.env 檔(儲存密鑰)

JWT_SECRET=mysecretkey123

routes/auth.js

import express from 'express';
import jwt from 'jsonwebtoken';
import dotenv from 'dotenv';

dotenv.config();
const router = express.Router();

// 模擬使用者登入
router.post('/login', (req, res) => {
const { username, password } = req.body;

// 模擬帳密驗證(實際應從 DB 查詢)
if (username === 'admin' && password === '123456') {
const payload = {
userId: 'abc123',
username: 'admin',
};

// 簽發 Token,過期時間為 1 小時
const token = jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: '1h',
});

return res.json({ token });
}

res.status(401).json({ error: '帳號或密碼錯誤' });
});

// 受保護資源
router.get('/profile', (req, res) => {
const authHeader = req.headers.authorization;

if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: '請提供 Token' });
}

const token = authHeader.split(' ')[1];

try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
res.json({ message: '驗證成功', user: decoded });
} catch (err) {
res.status(401).json({ error: 'Token 無效或過期' });
}
});

export default router;

index.js

import express from 'express';
import authRouter from './routes/auth.js';

const app = express();
app.use(express.json());

app.use('/api', authRouter);

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}`);
});

測試流程

  1. 發送 POST /api/login 並附上正確帳密(如 admin / 123456),取得 JWT。
  2. 將該 JWT 作為 Authorization: Bearer <token> 放入 Header 中,請求 GET /api/profile
  3. 若驗證成功,API 會回傳對應使用者資訊;若失敗,則回傳錯誤訊息。

常見安全注意事項

  • 使用 HTTPS:JWT 應透過 HTTPS 傳輸,避免中間人攻擊。
  • 設定適當過期時間:避免長期有效的 Token 被盜用。
  • 避免儲存敏感資訊於 Payload:Payload 是可被解碼的(雖然不可修改),不應含有密碼、信用卡資訊等。
  • 支援 Token 失效機制(如 Token 黑名單):JWT 是無狀態的,若要強制登出或封鎖,需額外設計邏輯。

總結

JWT 是實作登入與驗證的重要工具,具有無狀態、跨平台、可擴充的特性,特別適合 API 驗證場景。本文透過簡單的 Node.js + Express 實作,展示如何產生與驗證 JWT,並說明常見應用與安全注意事項。

不論你是單頁應用 SPA 開發者,還是撰寫 RESTful API 的後端工程師,掌握 JWT 都將大幅提升你的系統安全與擴充能力。