iT邦幫忙

2025 iThome 鐵人賽

DAY 19
0
Build on AWS

一步步帶你認識 Cloud Native —— 用AWS免費服務打造雲原生專案系列 第 19

Day19 Cognito + API Gateway + Lambda | 完全Serverless的用戶系統串接 (下)

  • 分享至 

  • xImage
  •  

上一篇文章中,我們介紹了API Gateway 以及 Lambda 這組常用的 Serverless 後端服務。並透過最小專案走過了 Cognito 發 Token → API Gateway 驗證 → Lambda 取得使用者資訊 的流程。
今天,我們來進入Console 建立 API Gateway 與 Lambda,進入 Cognito 串接的細節,走過整個登入流程建置可能會遇到的問題。


Step 1. 建立 Lambda (Console)

  1. 打開 AWS Console → Lambda → Create Function
  2. 選擇「Author from scratch」(同時也有依照預設模板 or 容器映像檔的方式部署)
    • Function name: DemoFunction
    • Runtime: Python 3.12

https://ithelp.ithome.com.tw/upload/images/20250829/20178103U52jWyh25I.png

值得注意的是在 Permission 區域提及會同時創建一個 Role 使得 Lambda 有權限將 Log 傳送至 Cloudwatch,這同時也代表在創建 Lambda 的同時將會創建一個 Cloudwatch 來監控這個執行實例。

  1. 在程式碼編輯區填寫簡單程式(回傳使用者身份)

https://ithelp.ithome.com.tw/upload/images/20250829/20178103DZbdbHOkfg.png

在 Lambda 程式碼區域撰寫程式時 可以透過"#"方式註解與 Amazon Q (AWS的AI助手)對話,他會根據你的需求以及上下文將程式碼補全 (例如用#要求補全 CORS 標頭)

你可能會發現 Lambda 本身還提供 Function URL 的配置,可為你的 Function 提供 Endpoint,不過與API Gateway 相比,Function URL 還是有它的限制,既然都在 Free Tier 為何不選好的呢?

功能 Lambda Function URL API Gateway
端點數量 每個 URL 綁定一個 Lambda(沒有內建多路由) 一個 Gateway 可管理多個路由與方法(RESTful / WebSocket / HTTP API)
授權方式 IAM / 自行在程式碼中實作 JWT 驗證 IAM / Cognito / Lambda Authorizer / API Key / WAF
CORS 支援,但配置較簡單 完整設定,能細緻控制來源、方法、Header
進階功能 無 (要在 Lambda 程式碼中處理驗證/轉換) 支援 Throttling、Rate limiting、流量管理、整合 WAF、安全性更完整
使用情境 Demo、小工具、內部服務、單一端點 正式專案、需要多路由、需要雲原生 API 管理與安全控制

而程式碼的維護通常會在自己的本地 IDE 可以透過容器 or CDK 將程式碼給封裝起來串接 CI/CD 流程 (這裡用CDK)


Step 2. 建立 API Gateway (Console)

  1. 打開 API Gateway → Create API
    • 選擇「HTTP API」(據 AWS 所說比起REST API 更加輕量化便宜)
  2. 給這個 API Gateway 一個名字
  3. Integration 指向剛剛建立的 Lambda

https://ithelp.ithome.com.tw/upload/images/20250829/20178103NfmBSjLRTf.png

4.建立一個 Route 建立了一個 GET 方法在 /Hello
https://ithelp.ithome.com.tw/upload/images/20250829/20178103MRzEa9yiNE.png

到這裡就成功建立了一個與 Lambda 連接的 API Gateway,只要透過 /Hello 端口就會觸發部署在 Lambda 中的 Function,而接下來我們要設定 Cognito 的 Token 驗證

在 AWS Console 中 Integration with lambda 會自動為 API Gateway 建立所需的權限,但在 CDK 需要額外配置

  1. 在「Authorization」選擇 JWT Authorizer

https://ithelp.ithome.com.tw/upload/images/20250829/20178103JJbREEKA3O.png

6.綁定已經建立的 User Pool

https://ithelp.ithome.com.tw/upload/images/20250829/20178103myP0KCwHJi.png

我們需要回到 Cognito 的 Console 找到Token signing key URL貼給 API Gateway 並指定 Authorization Header 驗證 Token

https://ithelp.ithome.com.tw/upload/images/20250829/20178103zEFV8b6a8a.png

Issuer URL的部分記得將複製下來的後綴 /.well-known/jwks.json 給去掉, Audience 的部分則是填入 App client 的 Client ID

完成後,就可以在前端帶著 JWT Token 呼叫這個 API


Step 3. 用 CDK 建立 API Gateway + Lambda

除了 Console 點點點之外,我們也能用 CDK 自動化部署
在這裡我們以昨天的Demo專案為範例。

在以下的程式碼中建立了一個 Lambda,並透過 API Gateway 連接,最後綁上 Cognito Authorizer。

from aws_cdk import (
    Stack,
    aws_apigateway as apigw,
    aws_lambda as _lambda,
    aws_cognito as cognito,
    aws_iam as iam,
    CfnOutput,
)

from constructs import Construct

from typing import Optional, Dict, Any

class JwtTestStack(Stack):
    def __init__(self, scope: Construct, construct_id: str, *, env_vars: Optional[Dict[str, Any]] = None, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)
        env_vars = env_vars or {}

        # 從環境變數讀取 User Pool Id(.env -> app.py 載入)
        user_pool_id = env_vars.get("REACT_APP_COGNITO_USER_POOL_ID", "ap-east-1_xxxxxxxxx")
        user_pool = cognito.UserPool.from_user_pool_id(self, "ImportedUserPool", user_pool_id)
        
        # 透過 CDK 定義的 Cognito User pool 資源直接定義授權方
        authorizer = apigw.CognitoUserPoolsAuthorizer(self, "JwtAuthorizer", cognito_user_pools=[user_pool])
        
        # 部署 Lambda 資源,程式碼則是從檔案路徑中引入
        fn = _lambda.Function(self, "JwtEchoFunction",
            runtime=_lambda.Runtime.PYTHON_3_12,
            handler="handler.lambda_handler",
            code=_lambda.Code.from_asset("../lambda/jwt_test")
        )
        # 部署 API Gateway 資源
        api = apigw.RestApi(
            self,
            "JwtTestApi",
            # 這裡是定義為REST API
            rest_api_name="JWT Test API",  
            description="Echo JWT claims",
            
            # 定義CORS規則
            default_cors_preflight_options=apigw.CorsOptions(
                allow_origins=apigw.Cors.ALL_ORIGINS,
                allow_methods=["GET", "OPTIONS"],
                allow_headers=["Authorization", "Content-Type"]
            )
        )
        
        # 創建 ROUTE /echo 並綁定 Lamnbda Function

        echo = api.root.add_resource("echo")
        echo_lambda = apigw.LambdaIntegration(fn)

        echo.add_method("GET", echo_lambda,
            authorizer=authorizer,
            authorization_type=apigw.AuthorizationType.COGNITO
        )

        # 明確授權 API Gateway 調用 Lambda(避免權限錯誤)
        fn.add_permission(
            "ApiGatewayInvoke",
            principal=iam.ServicePrincipal("apigateway.amazonaws.com"),
            action="lambda:InvokeFunction",
            source_arn=f"arn:aws:execute-api:{Stack.of(self).region}:{Stack.of(self).account}:{api.rest_api_id}/*/GET/echo"
        )

        CfnOutput(self, "ApiUrl", value=api.url)



常見錯誤排除

1. 401 Unauthorized

  • 原因:JWT Token 不正確或沒有帶上
  • 解法:
    • 前端 fetch 呼叫 API 時,要記得加上 Authorization: Bearer <token>
    • Token 必須是 ID TokenAccess Token (預設為ID Token)

2. CORS 問題

  • 常見錯誤:CORS policy: No 'Access-Control-Allow-Origin'
  • 解法:
    • 在 API Gateway → CORS → 啟用 CORS
    • 記得允許 Authorization header

3. API Gateway 權限問題

  • 錯誤:API Gateway 沒有 Lambda 執行權限
  • 解法:
    • API Gateway 呼叫 Lambda 需要 lambda:InvokeFunction
    • CDK 會自動加上,但若手動設定要檢查 Lambda Resource Policy

小結

今天我們完成了:

  1. 建立一個 Lambda 後端 API
  2. 用 API Gateway 串接 Lambda,並設定 Cognito 驗證
  3. 讓「登入後的使用者」才能呼叫 API
  4. 學會排解常見的 401 / CORS / 權限錯誤

到這裡,我們的「安全守門員」就正式上線了 🎉


上一篇
Day18 Cognito + API Gateway + Lambda | 完全Serverless的用戶系統串接 (上)
下一篇
Day20 用 AWS CloudWatch 來觀測與除錯你的 Serverless API | Log Group、Log Stream、Events
系列文
一步步帶你認識 Cloud Native —— 用AWS免費服務打造雲原生專案23
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言