iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 2
0
Modern Web

宅男的浪漫 - 用 .NET Core 打造 Line 婚禮聊天機器人經驗分享系列 第 2

用.NET Core接收第一個Line訊息

第12 屆iT邦幫忙鐵人賽系列文章 (Day2)

基本設定

關於如何註冊一個串接自己Web Api 的 Line Chatbot 網路上已經很多的教學文了,大概分為以下幾個步驟

https://developers.line.biz/en/

[ ] 申請一個Provider
[ ] 申請一個Channel (一個Provider可以有多個Channel)
[ ] 於Channel取得 Channel Secret 及 Access Token
[ ] Use webhook 啟用
[ ] Auto-reply messages 關閉
[ ] 設定 Webhook URL (需為https)

起始 Web API 專案與 Line Server 做連結

建立一個 .NET Core 3.1 的 Web Api 專案

建立一個 Line Controller,先用 dynamic 接收,看看 Request 過來的是什麼

執行看看,weatherforecast 為初始專案的 Sample,此Local Api 的 Port 為 44300

透過 ngrok 讓 Line Server 跟 Local Web API 做連結

  1. 安裝 node.js

  2. npm install ngrok -g

  3. ngrok -v

本文章所使用的版本為 2.3.35

執行 ngrok http 44300 -host-header=”localhost:44300" -region ap

44300要替換成你本地執行Run起來的Port

-region ap 為切換 ngrok 的 server,預設為us,在今年 Line 開始阻擋來至於 ngrok us 的 server 了,所以我們要將 Region 切到 Asia/Pacific (ap)

Run 起來大概會長這樣

我們將 https://44a914411bf2.ap.ngrok.io 這串加上Route api/line 後貼到 Line Developer Console,點選 Verify

進入 http://127.0.0.1:4040/ ,可以看到Line Server POST 過來的 Request

把這個POST 的 Body 移到 Postman 方便測試

也可以直接用我建好的 Collection 匯入 https://www.getpostman.com/collections/8a93e0b6ce3ff66f8c8b

透過 Postman 測試,在VS下中斷點,驗證可接收到

驗證是否為有效的Line來源

API 有了,但我們要如何驗證這個 Request 是來自 Line Server 來阻擋惡意攻擊呢? 在 Line Server 所傳過來的 Header 會包含這個項目 **X-Line-Signature ,**將 POST Body 跟 Chanel Secret 用做 HMAC-SHA256 演算法Hash過後,比對過後如果相同,表示該 Request 是來自 Line Server

*Verifying Signatures 官方參考文件*

在 Web Api 實作,我們先新建一個 Authorization Filter 來驗證 Line Signature,.NET Core Web Api 的 Reuqest pipeline 如下:

https://docs.microsoft.com/zh-tw/aspnet/core/mvc/controllers/filters?view=aspnetcore-3.1https://docs.microsoft.com/zh-tw/aspnet/core/mvc/controllers/filters?view=aspnetcore-3.1

新增一個 Filter 叫 LineVerifySignatureFilter.cs 繼承 IAuthorizationFilter 實作,Channel Secret 要替換

public class LineVerifySignatureFilter : IAuthorizationFilter
{
 public void OnAuthorization(AuthorizationFilterContext context)
 {
  /*
   * Ensure the requestBody can be read multiple times. 
   * [https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.httprequestrewindextensions.enablebuffering?view=aspnetcore-3.1](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.httprequestrewindextensions.enablebuffering?view=aspnetcore-3.1)
   */
  context.HttpContext.Request.EnableBuffering();

string requestBody = new StreamReader(context.HttpContext.Request.Body).ReadToEndAsync().Result;
  context.HttpContext.Request.Body.Position = 0;

var xLineSignature = context.HttpContext.Request.Headers["X-Line-Signature"].ToString();
  var channelSecret = Encoding.UTF8.GetBytes("{Channel Secret}");
  var body = Encoding.UTF8.GetBytes(requestBody);

using (HMACSHA256 hmac = new HMACSHA256(channelSecret))
  {
   var hash = hmac.ComputeHash(body, 0, body.Length);
   var xLineBytes = Convert.FromBase64String(xLineSignature);
   if (SlowEquals(xLineBytes, hash) == false)
   {
    context.Result = new ForbidResult();
   }
  }
 }

private static bool SlowEquals(byte[] a, byte[] b)
 {
  uint diff = (uint)a.Length ^ (uint)b.Length;
  for (int i = 0; i < a.Length && i < b.Length; i++)
   diff |= (uint)(a[i] ^ b[i]);
  return diff == 0;
 }
}

新增一個 Line LineVerifySignatureAttribute.cs 透過 TypeFilterAttribute 來例化

public class LineVerifySignatureAttribute : TypeFilterAttribute
{
 public LineVerifySignatureAttribute() : base(typeof(LineVerifySignatureFilter))
 {
 }
}

在 Line Api Route 加上 [LineVerifySignature] 進行驗證,這樣程式就乾淨多了

把 Channel Secret/Access Token 移到 appsettings.json

通常我們會把這兩個資訊寫在設定檔,以便使用跟替換,在 .NET Core 可以使用 IOption 的方式來從設定檔注入

在 appsettings.json 增加區塊,放Channel Secret/Access Token

增加一個 LineSetting.cs,我們要用強型別來開發

startup.cs 增加 services.AddOptions()

回到前面建立的 LineVerifySignatureFilter.cs,注入 LineSetting及使用 ChannelSecret 即可完成

懶人包,本次學到了什麼?

本篇文章同步發佈於我的 Medium 如果這篇文章對你有幫助,就大力追蹤和拍手鼓掌下去吧!


上一篇
用.NET Core與Line Bot製作婚禮機器人
下一篇
用 .NET Core 接收Line的訊息事件
系列文
宅男的浪漫 - 用 .NET Core 打造 Line 婚禮聊天機器人經驗分享30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
SoonYang
iT邦新手 5 級 ‧ 2020-10-20 21:28:50

「增加一個 LineSetting.js,我們要用強型別...」
嗨你好,這邊應是 .cs

Kyle Shen iT邦新手 2 級 ‧ 2020-10-21 09:26:21 檢舉

調整了 感謝 !

我要留言

立即登入留言