第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)
建立一個 .NET Core 3.1 的 Web Api 專案
建立一個 Line Controller,先用 dynamic 接收,看看 Request 過來的是什麼
執行看看,weatherforecast 為初始專案的 Sample,此Local Api 的 Port 為 44300
透過 ngrok 讓 Line Server 跟 Local Web API 做連結
安裝 node.js
npm install ngrok -g
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下中斷點,驗證可接收到
API 有了,但我們要如何驗證這個 Request 是來自 Line Server 來阻擋惡意攻擊呢? 在 Line Server 所傳過來的 Header 會包含這個項目 **X-Line-Signature ,**將 POST Body 跟 Chanel Secret 用做 HMAC-SHA256 演算法Hash過後,比對過後如果相同,表示該 Request 是來自 Line Server
在 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.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] 進行驗證,這樣程式就乾淨多了
通常我們會把這兩個資訊寫在設定檔,以便使用跟替換,在 .NET Core 可以使用 IOption 的方式來從設定檔注入
在 appsettings.json 增加區塊,放Channel Secret/Access Token
增加一個 LineSetting.cs,我們要用強型別來開發
startup.cs 增加 services.AddOptions()
回到前面建立的 LineVerifySignatureFilter.cs,注入 LineSetting及使用 ChannelSecret 即可完成
如何申請一個 Line Chatbot - 參考上方6步驟
LINE Developers
如何建立一個 .NET Core Web Api 專案
Building Your First Web API with ASP.NET Core MVC and Visual Studio
用 CLI 建立 .NET Core
如何將 Web API 跟 Line Server 連結起來
Install ngrok by npm
如何驗證有效的Line Request
Line Signature Validation
如何使用 .NET Core 的 Filters & Attributes
Filters in ASP.NET Core
如何在.NET Core讀取設定檔
Options pattern in ASP.NET Core
本篇文章同步發佈於我的 Medium 如果這篇文章對你有幫助,就大力追蹤和拍手鼓掌下去吧!
「增加一個 LineSetting.js,我們要用強型別...」
嗨你好,這邊應是 .cs
調整了 感謝 !