跳至主要内容

4 篇文章 含有標籤「JWT」

檢視所有標籤

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

· 閱讀時間約 3 分鐘
KD Chang
Builder, Curator & Lifelong Learner

前言​

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 分鐘
KD Chang
Builder, Curator & Lifelong Learner

前言​

當你要將 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 並集中處理。
  • 可搭配 jsonwebtoken、axios-auth-refresh 等工具。
  • 若前後端完全分離,建議使用跨網域 Cookie 搭配 SameSite=None; Secure。

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

· 閱讀時間約 4 分鐘
KD Chang
Builder, Curator & Lifelong Learner

一、JWT 基本安全策略​

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

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

2. 設定有效期限(exp)​

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

3. 限制 Token 權限與資訊​

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

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 分鐘
KD Chang
Builder, Curator & Lifelong Learner

前言​

在現代 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 都將大幅提升你的系統安全與擴充能力。