在 Serverless 架構中,API Gateway 常作為前端應用程式的入口,因此正確設定 CORS 至關重要。本文除了 AWS CLI 的操作步驟,也提供 CloudFormation 的組態方式,讓 CORS 設定更簡潔易於管理。此外,現代 Web 應用程式多仰賴前端 JavaScript 與後端服務互動,因此文章也詳細說明如何使用 JavaScript SDK 與 Cognito 進行使用者管理,包含註冊、驗證和登入等關鍵流程,並提供可直接在 CodePen 測試的程式碼範例,有助於快速上手 Cognito 的前端整合。
實作 CORS 的 Serverless API
在開發 Serverless 架構的 RESTful API 時,CORS(Cross-Origin Resource Sharing)是一個重要的議題。CORS 允許網頁從不同的網域請求資源,這對於現代網頁應用非常重要。本篇文章將介紹如何使用 AWS CLI 和 CloudFormation 設定 CORS。
使用 AWS CLI 設定 CORS
要使用 AWS CLI 設定 CORS,需要執行以下步驟:
建立 API:首先,建立一個新的 API Gateway。
aws apigateway create-rest-api --name 'My API'取得根資源 ID:取得根資源的 ID。
aws apigateway get-resources --rest-api-id xenqybowjg建立資源和子資源:建立一個新的資源和子資源。
aws apigateway create-resource --rest-api-id xenqybowjg --parent-id sfgfk6 --path-part greeting aws apigateway create-resource --rest-api-id xenqybowjg --parent-id sfgfk6 --path-part '{name}'設定 GET 方法:
- 建立 GET 方法:
aws apigateway put-method --rest-api-id xenqybowjg --resource-id sfgfk6 --http-method GET --authorization 'NONE' - 設定 GET 方法回應:
aws apigateway put-method-response --rest-api-id xenqybowjg --resource-id sfgfk6 --http-method GET --status-code 200 --response-parameters "method.response.header.Access-Control-Allow-Origin=true" - 設定整合請求:
aws apigateway put-integration --rest-api-id xenqybowjg --resource-id sfgfk6 --http-method GET --type MOCK --integration-http-method GET --request-templates "{\"application/json\":\"{\"statusCode\": \"200\"}\"}" - 設定整合回應:
aws apigateway put-integration-response --rest-api-id xenqybowjg --resource-id sfgfk6 --http-method GET --status-code 200 --response-parameters "method.response.header.Access-Control-Allow-Origin='*'"
- 建立 GET 方法:
設定 OPTIONS 方法(用於 CORS 預檢請求):
- 建立 OPTIONS 方法:
aws apigateway put-method --rest-api-id xenqybowjg --resource-id sfgfk6 --http-method OPTIONS --authorization 'NONE' - 設定 OPTIONS 方法回應:
aws apigateway put-method-response --rest-api-id xenqybowjg --resource-id sfgfk6 --http-method OPTIONS --status-code 200 --response-parameters "method.response.header.Access-Control-Allow-Origin=true,method.response.header.Access-Control-Allow-Headers=true" - 設定整合請求:
aws apigateway put-integration --rest-api-id xenqybowjg --resource-id sfgfk6 --http-method OPTIONS --type MOCK --integration-http-method OPTIONS --request-templates "{\"application/json\":\"{\"statusCode\": \"200\"}\"}" - 設定整合回應,使用
put-method-integration-response-options.json檔案:{ "method.response.header.Access-Control-Allow-Origin": "'*'", "method.response.header.Access-Control-Allow-Headers": "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'" }aws apigateway put-integration-response --rest-api-id xenqybowjg --resource-id sfgfk6 --http-method OPTIONS --status-code 200 --response-parameters file://put-method-integration-response-options.json
- 建立 OPTIONS 方法:
佈署 API:
aws apigateway create-deployment --rest-api-id xenqybowjg --stage-name dev
使用 CloudFormation 設定 CORS
CloudFormation 提供了一種更簡潔的方式來定義和佈署 AWS 資源。你可以使用 AWS::ApiGateway::Method 資源型別來定義 CORS 設定。
程式碼範例與解析
Resources:
MyApi:
Type: 'AWS::ApiGateway::RestApi'
Properties:
Name: !Sub 'my-api-${AWS::Region}'
MyResource:
Type: 'AWS::ApiGateway::Resource'
Properties:
RestApiId: !Ref MyApi
ParentId: !GetAtt MyApi.RootResourceId
PathPart: 'greeting'
MyMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
RestApiId: !Ref MyApi
ResourceId: !Ref MyResource
HttpMethod: GET
Authorization: NONE
Integration:
HttpMethod: GET
Type: MOCK
RequestTemplates:
application/json: |
{"statusCode": "200"}
Responses:
- StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
MyOptionsMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
RestApiId: !Ref MyApi
ResourceId: !Ref MyResource
HttpMethod: OPTIONS
Authorization: NONE
Integration:
HttpMethod: OPTIONS
Type: MOCK
RequestTemplates:
application/json: |
{"statusCode": "200"}
Responses:
- StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'"
內容解密:
- MyApi 和 MyResource 資源定義:定義了一個名為
MyApi的 API Gateway 和一個名為MyResource的資源。 - MyMethod 和 MyOptionsMethod 定義:定義了 GET 和 OPTIONS 方法,並設定了 MOCK 型別的整合請求和回應引數,以支援 CORS。
RequestTemplates和Responses設定:在整合請求中定義了回應範本,並在整合回應中設定了 CORS 相關的標頭引數。
使用 JavaScript SDK 實作和測試 Cognito 操作
在第四章「使用 Amazon Cognito 進行應用程式安全」中,我們已經瞭解瞭如何使用 AWS CLI 命令操作 Cognito。由於 Web 應用程式通常會在前端使用 JavaScript SDK 與 Cognito 互動,因此本章節將介紹如何使用 JavaScript SDK 進行 Cognito 操作,並使用 CodePen 進行測試。
準備工作
在開始之前,請確保您已經完成以下準備工作:
- 擁有一個可用的 AWS 帳戶。
- 在您的機器上安裝了 Node.js 和 npm。
- 下載
amazon-cognito-identity.min.js檔案。 - 建立一個 S3 儲存桶並上傳
amazon-cognito-identity.min.js檔案。 - 建立一個 Cognito 使用者池和客戶端。
下載 amazon-cognito-identity.min.js 檔案
首先,建立一個臨時資料夾並進入該資料夾,然後執行以下命令:
npm i amazon-cognito-identity-js
執行後,您應該會看到類別似以下的回應:
added 1 package in 2s
建立 S3 儲存桶並上傳 amazon-cognito-identity.min.js 檔案
建立一個 S3 儲存桶:
aws s3api create-bucket --bucket serverlesscookbook-cognito-files --profile admin
上傳 amazon-cognito-identity.min.js 檔案:
aws s3 cp amazon-cognito-identity.min.js s3://serverlesscookbook-cognito-files --profile admin
執行儲存桶政策以允許公開讀取存取:
aws s3api put-bucket-policy --bucket serverlesscookbook-cognito-files --policy file://s3-website-policy.json --profile admin
s3-website-policy.json 檔案的內容如下:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObjectAccess",
"Effect": "Allow",
"Principal": "*",
"Action": ["s3:GetObject"],
"Resource": ["arn:aws:s3:::serverlesscookbook-cognito-files/*"]
}
]
}
建立 Cognito 使用者池和客戶端
建立一個 Cognito 使用者池:
aws cognito-idp create-user-pool --cli-input-json file://create-user-pool-cli-input.json --region us-east-1 --profile admin
create-user-pool-cli-input.json 檔案的內容如下:
{
"PoolName": "javscript_pool",
"Policies": {
"PasswordPolicy": {
"MinimumLength": 8,
"RequireUppercase": true,
"RequireLowercase": true,
"RequireNumbers": true,
"RequireSymbols": true
}
},
"AutoVerifiedAttributes": ["email"],
"AliasAttributes": ["email"],
"EmailVerificationMessage": "Your verification code from MyApp is {####}",
"EmailVerificationSubject": "Your verification code from MyApp",
"UserPoolTags": {
"Team": "Dev"
}
}
建立一個使用者池客戶端:
aws cognito-idp create-user-pool-client --user-pool-id us-east-1_P8srRzYqn --client-name javscript-pool-client --explicit-auth-flows USER_PASSWORD_AUTH --region us-east-1 --profile admin
請將 user-pool-id 的值替換為您在前一步驟中建立的使用者池 ID。
使用 CodePen 執行 JavaScript SDK 程式碼
- 開啟 CodePen 並新增所需檔案的位置。
- 前往 https://codepen.io/。
- 點選「Create」標籤,然後選擇「Pen」選項。
- 在新視窗中,點選「Settings」選單,選擇「Behaviour」標籤,然後取消勾選「Auto-Updating Preview」下的「Enabled」選項。
- 在「Settings」選單中,選擇「JavaScript」標籤,然後執行以下操作:
- 搜尋「aws sdk」並選擇適當的 SDK。
- 新增
amazon-cognito-identity.min.js檔案的 URL(例如:https://s3.amazonaws.com/cognito-min-bucket/amazon-cognito-identity.min.js)。
使用 JavaScript SDK 進行 Cognito 操作
使用者註冊
var poolData = {
UserPoolId: '<user pool id>',
ClientId: '<client id>'
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
var attributeList = [];
var emailAttribute = {
Name: 'email',
Value: '<user email>'
};
attributeList.push(new AmazonCognitoIdentity.CognitoUserAttribute(emailAttribute));
userPool.signUp('heartin', 'Passw0rd$1', attributeList, null, function(err, result) {
if (err) {
console.log(JSON.stringify(err));
alert(err.message || JSON.stringify(err));
return;
}
var cognitoUser = result.user;
console.log('user name is ' + cognitoUser.getUsername());
});
詳細解說:
- 建立使用者池物件:使用
AmazonCognitoIdentity.CognitoUserPool建構函式建立一個使用者池物件,需要提供UserPoolId和ClientId。 - 定義使用者屬性:建立一個屬性列表,並新增電子郵件屬性。
- 註冊使用者:呼叫
signUp方法註冊使用者,需要提供使用者名稱、密碼、屬性列表和其他引數。
確認註冊的使用者
var poolData = {
UserPoolId: '<user pool id>',
ClientId: '<client id>'
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
var userData = {
Username: 'heartin',
Pool: userPool
};
var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.confirmRegistration('698099', true, function(err, result) {
if (err) {
alert(err.message || JSON.stringify(err));
return;
}
console.log('call result: ' + result);
});
詳細解說:
- 建立使用者物件:使用
AmazonCognitoIdentity.CognitoUser建構函式建立一個使用者物件,需要提供使用者名稱和使用者池物件。 - 確認註冊:呼叫
confirmRegistration方法確認註冊,需要提供驗證碼和其他引數。
登入應用程式
var authenticationData = {
Username: 'heartin',
Password: 'Passw0rd$1',
};
var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
var poolData = {
UserPoolId: '<user pool id>',
ClientId: '<client id>'
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
var userData = {
Username: 'heartin',
Pool: userPool
};
var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function(result) {
console.log('access token + ' + result.getAccessToken().getJwtToken());
},
onFailure: function(err) {
alert(err.message || JSON.stringify(err));
},
});
詳細解說:
- 建立驗證資料:建立一個驗證資料物件,需要提供使用者名稱和密碼。
- 建立驗證詳細資訊:使用
AmazonCognitoIdentity.AuthenticationDetails建構函式建立一個驗證詳細資訊物件,需要提供驗證資料物件。 - 登入使用者:呼叫
authenticateUser方法登入使用者,需要提供驗證詳細資訊物件和其他引數。