前言
測試 Pub/Sub Subscription 傳送類型 = Push
由 Client (我方) 提供 API Endpoint 供 Pub/Sub 呼叫
- Endpoint 需為公開的 https address,且為有效 SSL 憑證
- 可啟用身分驗證,啟用後 Pub/Sub 打我方 API 時,會在 header 帶 Authorization 的 JWT Token 供我方驗證,避免被亂呼叫
- 收到消息後,若要確認消息則需回傳 http status code 102、200、201、202、204
前置作業
API 建立
預先準備好一個 .Net Web API 專案,建立 API 用以提供 GCP Pub/Sub 呼叫,並驗證 JWT Token
using Google.Apis.Auth;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
namespace GCP_PubSubConsumerPush.Controllers
{
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
private readonly ILogger<TestController> _logger;
public TestController(ILogger<TestController> logger)
{
_logger = logger;
}
/// <summary>
/// Handle authenticated push request coming from pubsub.
/// </summary>
[HttpPost]
[Route("/AuthPush")]
public async Task<IActionResult> AuthPushAsync([FromBody] PushBody body, [FromQuery] string token)
{
try
{
string bodyJson = JsonConvert.SerializeObject(body);
string authorizaionHeader = HttpContext.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(authorizaionHeader))
{
string authToken = authorizaionHeader.StartsWith("Bearer ") ? authorizaionHeader.Substring(7) : string.Empty;
// Verify and decode the JWT.
var payload = await JsonWebSignature.VerifySignedTokenAsync<PubSubPayload>(authToken);
string payloadJson = JsonConvert.SerializeObject(payload);
}
string verificationToken = token ?? body.message.attributes["token"];
if (verificationToken != "123")
return new BadRequestResult();
var messageBytes = Convert.FromBase64String(body.message.data);
string message = System.Text.Encoding.UTF8.GetString(messageBytes);
return new OkResult();
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
}
public class PubSubPayload : JsonWebSignature.Payload
{
[JsonProperty("email")]
public string Email { get; set; }
[JsonProperty("email_verified")]
public string EmailVerified { get; set; }
}
public class PushBody
{
public PushMessage message { get; set; }
public string subscription { get; set; }
}
public class PushMessage
{
public Dictionary<string, string> attributes { get; set; }
public string data { get; set; }
public string message_id { get; set; }
public string publish_time { get; set; }
}
}
透過 ngrok 提供外部呼叫 (本機測試方便使用)
可透過免費的 ngrok 服務,可將外部 request 轉發到本機,且 ngrok 有自帶 https 憑證
5208 為 本機測試的 API Port號
ngrok http 5208
備註: 若一直收不到訊息,且 ngrok 顯示 307 Temporary Redirect,則把 app.UseHttpsRedirection() 註解掉即可
建立 傳送類型 = push 的 subscription
可參考GCP 官方文件 - Push subscriptions
# 因要建立 Token 需先將服務帳戶賦予權限
gcloud projects add-iam-policy-binding $projectId --member="serviceAccount:leo-pubsub@$projectId.iam.gserviceaccount.com" --role="roles/iam.serviceAccountTokenCreator"
# 建立 subscription
gcloud pubsub subscriptions create Test-Topic-SubPush \
--topic=Test-Topic \
--push-endpoint=https://d993-116-241-108-66.ngrok-free.app/AuthPush?token=123 \
--push-auth-service-account=leo-pubsub@$projectId.iam.gserviceaccount.com \
--ack-deadline=10
push-endpoint = 我方的 API endpoint,後面加的 token 可隨機產生,可在 API 內再次驗證
API 內除了驗證 Google Authorization 的 JWT Token之外,也可再次驗證自定義的 token(123)
body 內容
{
"message": {
"attributes": {
"test": "888"
},
"data": "MTIz",
"message_id": "7743243870023401",
"publish_time": "2023-05-20T08:55:02.649Z"
},
"subscription": "projects/$projectId/subscriptions/Test-Topic-SubPush"
}
data 是 base64字串,需要轉為 byte 再轉回 string
JWT Token 解析後的 payload 內容
{
"email": "leo-pubsub@$projectId.iam.gserviceaccount.com",
"email_verified": "true",
"iss": "https://accounts.google.com",
"sub": "104310064111477055168",
"aud": "https://d993-116-241-108-66.ngrok-free.app/AuthPush?token=123",
"target_audience": null,
"exp": 1684578888,
"nbf": null,
"iat": 1684575288,
"jti": null,
"nonce": null,
"typ": null
}
參考文件
GCP 官方文件 - Push subscriptions
GCP 官方文件 - Authentication for push subscription
轉載請註明來源,若有任何錯誤或表達不清楚的地方,歡迎在下方評論區留言,也可以來信至 leozheng0621@gmail.com
如果文章對您有幫助,歡迎斗內(donate),請我喝杯咖啡