API 認證機制(Authentication)

是什麼?

API 認證(Authentication)是驗證「你是誰」的機制。不同的認證方式在安全性、複雜度、適用場景上各有取捨。JWT 適合無狀態認證、OAuth 2.0 適合第三方授權、API Key 適合服務間通訊。

ℹ️認證 vs 授權

Authentication(認證)= 你是誰?驗證身份。Authorization(授權)= 你能做什麼?檢查權限。認證在前,授權在後。本篇聚焦認證,授權會在 IAM 章節深入討論。

核心觀念

常見誤區

⚠️常見誤區

  • 把敏感資料放在 JWT Payload:JWT 的 Payload 只是 Base64 編碼(不是加密),任何人都能解碼讀取。不要放密碼、信用卡號等敏感資訊
  • JWT 不設過期時間:沒有過期時間的 JWT 一旦洩漏就永久有效。Access Token 建議 15-60 分鐘過期
  • 用 API Key 做使用者認證:API Key 適合服務對服務(S2S)通訊,不適合終端使用者認證(因為無法追蹤個別使用者)

流程/步驟

1

使用者登入

送出帳號密碼到 /auth/login

2

伺服器驗證

比對密碼 Hash,驗證身份

3

產生 Token

簽發 JWT Access Token + Refresh Token

4

客戶端儲存

Access Token 存記憶體,Refresh Token 存 HttpOnly Cookie

5

帶 Token 請求

每次 API 請求帶 Authorization: Bearer token

6

Token 過期換新

用 Refresh Token 換新的 Access Token

流程解讀:使用者登入後取得一對 Token。Access Token 短效(15-60 分鐘)放在請求 Header 中,Refresh Token 長效(7-30 天)安全儲存在 HttpOnly Cookie。Access Token 過期時用 Refresh Token 無感刷新。

程式碼範例

C# 版本

csharp
// ASP.NET Core — JWT 認證設定
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = "https://api.example.com",
            ValidateAudience = true,
            ValidAudience = "https://app.example.com",
            ValidateLifetime = true,
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(config["Jwt:Secret"]!))
        };
    });
 
// 產生 JWT
public string GenerateToken(User user)
{
    var claims = new[]
    {
        new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
        new Claim(ClaimTypes.Email, user.Email),
        new Claim(ClaimTypes.Role, user.Role)
    };
 
    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_secret));
    var token = new JwtSecurityToken(
        issuer: "https://api.example.com",
        audience: "https://app.example.com",
        claims: claims,
        expires: DateTime.UtcNow.AddMinutes(30),
        signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
    );
 
    return new JwtSecurityTokenHandler().WriteToken(token);
}

TypeScript 版本

typescript
// Express.js — JWT 認證
import jwt from "jsonwebtoken";
 
// 產生 Token
function generateToken(user: User): string {
  return jwt.sign(
    { userId: user.id, email: user.email, role: user.role },
    process.env.JWT_SECRET!,
    { expiresIn: "30m", issuer: "https://api.example.com" }
  );
}
 
// 驗證 Middleware
function authMiddleware(req: Request, res: Response, next: NextFunction) {
  const token = req.headers.authorization?.replace("Bearer ", "");
  if (!token) return res.status(401).json({ error: "No token provided" });
 
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET!);
    req.user = decoded;
    next();
  } catch {
    res.status(401).json({ error: "Invalid or expired token" });
  }
}
 
// 保護路由
app.get("/api/profile", authMiddleware, (req, res) => {
  res.json({ user: req.user });
});

Python 版本

python
# FastAPI — JWT 認證
from jose import jwt, JWTError
from datetime import datetime, timedelta
from fastapi import Depends, HTTPException
from fastapi.security import HTTPBearer
 
security = HTTPBearer()
SECRET_KEY = "your-secret-key"
 
def create_access_token(user_id: int, role: str) -> str:
    payload = {
        "sub": str(user_id),
        "role": role,
        "exp": datetime.utcnow() + timedelta(minutes=30),
    }
    return jwt.encode(payload, SECRET_KEY, algorithm="HS256")
 
async def get_current_user(credentials=Depends(security)):
    try:
        payload = jwt.decode(credentials.credentials, SECRET_KEY, algorithms=["HS256"])
        return payload
    except JWTError:
        raise HTTPException(status_code=401, detail="Invalid token")
 
@app.get("/api/profile")
async def profile(user=Depends(get_current_user)):
    return {"user": user}

架構圖/概念圖

User / Client
login credentials
Auth Server
issues
Access Token (JWT)
Refresh Token
Resource API

使用者用帳密向 Auth Server 認證,取得 Access Token 和 Refresh Token。之後每次請求 API 帶上 Access Token。Token 過期時用 Refresh Token 向 Auth Server 換新的 Access Token。

實戰補充

Q: JWT 的 Secret Key 該怎麼管理?

A: 不要硬寫在程式碼中。用環境變數或 Secret Manager(如 Azure Key Vault、AWS Secrets Manager)管理。生產環境的 Key 長度至少 256 bits。考慮用 RSA 非對稱加密(RS256)取代對稱加密(HS256),這樣驗證方只需公鑰。

Q: OAuth 2.0 的四種 Grant Type 怎麼選?

A: Authorization Code:Web App、有後端的 SPA(最安全)。Authorization Code + PKCE:純前端 SPA、Mobile App。Client Credentials:服務對服務(S2S)。ImplicitResource Owner Password 已不推薦使用。

Q: 為什麼不用 localStorage 存 Token?

A: localStorage 容易被 XSS 攻擊讀取。Refresh Token 建議存在 HttpOnly + Secure + SameSite Cookie 中。Access Token 可存在記憶體(JavaScript 變數)中。

理解測驗

🤔 JWT 的 Payload 是加密的嗎?

🤔 服務對服務(S2S)的認證最適合用哪種方式?

🤔 Access Token 建議的過期時間是多長?

重點整理

💡一句話記住

JWT 無狀態驗身份、OAuth 委託授權、API Key 簡單但粗。 口訣:「短命 Access、長命 Refresh、密鑰不寫死」

| 方式 | 適用場景 | 優點 | 缺點 | |------|---------|------|------| | JWT | 使用者認證、微服務間 | 無狀態、不查 DB | 無法即時撤銷 | | OAuth 2.0 | 第三方登入、授權 | 標準化、細粒度授權 | 複雜度高 | | API Key | S2S、公開 API | 簡單 | 無法追蹤使用者 |

你可能也想看

分頁、過濾與排序GraphQL 入門

按 ← → 鍵切換課程