The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

如何使用 .NET Core 串接 e 政府服務平臺單一登入暨多因子驗證機制

我記得我最早在 2008 年就因為一個標案串接過 e 政府服務平臺的單一登入機制,事隔多年後,竟然什麼也沒有變,還是一樣透過 SOAP Web Service 來進行驗證。今天這篇文章我就來說說如何透過 .NET 7 實作 e 政府服務平臺的單一簽入機制,以及說明如何用 .NET 呼叫以 SOAP 為基礎的 Web Service 服務。

SSO

大致整合步驟

關於單一登入暨多因子驗證的相關文件,可以參考這裡:https://www.gsp.gov.tw/index-download-2.htm

基本上要完成單一簽入機制,只需要以下幾個步驟:

  1. 組成一個單一簽入網址,將使用者導向至 e 政府服務平臺單一登入網頁

    網址結構如下:

    https://www.cp.gov.tw/portal/Clogin.aspx?ReturnUrl={回呼網址}&level={驗證等級}

    注意: 這裡的 回呼網址 不能隨便填寫,在 e 政府服務平台會做域名驗證,你必須在機關單位申請單一簽入時就要指定好,未指定的網址將無法完成單一簽入作業。這裡只會驗證 Hostname (域名) 部分,不會驗證 SchemePortPathinfo 等部分。

    我是這樣實作的:

    void Main()
    {
        var url = GetLoginUrl("https://mysite.com.tw/callback", AuthLevel.帳號登入);
        //var url = GetLoginUrl("https://mysite.com.tw/callback", LoginType.憑證登入);
    }
    
    public string GetLoginUrl(string returnUrl, AuthLevel level)
    {
        var loginUrl = "https://www.cp.gov.tw/portal/Clogin.aspx";
    
        // 需安裝 Microsoft.AspNetCore.Http.Extensions 套件
        var qb = new QueryBuilder();
        qb.Add("ReturnUrl", returnUrl);
        qb.Add("level", level.ToString("D"));
    
        return $"{loginUrl}{qb.ToQueryString().Value}";
    }
    
    public enum AuthLevel
    {
        帳號登入 = 1,
        憑證登入 = 2
    }
    
  2. 使用者登入成功後,會透過 HTTP POST 回到 ReturnUrl 指定的網址,你要透過 application/x-www-form-urlencoded 的方式取得 twGovT1 傳入欄位的值。

    以下我用 ASP.NET Core 7 Minimal API 的語法示範如何取得 twGovT1 欄位的值 (也就是所謂的 Token1 值):

    app.MapPost("/callback", async Task<string> (HttpRequest request) =>
    {
        var formData = await request.ReadFormAsync();
        var formDataDictionary = formData.ToDictionary(x => x.Key, x => x.Value.ToString());
    
        return formDataDictionary["twGovT1"];
    })
    

    注意: 直到 ASP.NET Core 7.0 的 Minimal API 都還沒有支援 [FromForm] 的繫結方式,必須等 ASP.NET Core 8.0 才開始有支援 Binding to forms

  3. 取得 Token1 之後,還要呼叫一個以 SOAP 為主的 Web Service,取得使用者基本資料,這才算完成單一簽入流程。

    這個步驟稍微複雜些,我將在下一小節說明。

    注意: 由於 ServiceID 必須要透過機關單位申請後才能取得,而且該 Web Service 在呼叫時會驗證指定的來源 IP 地址,若申請單一簽入時沒有申請到正確的 IP 地址,將無法正確的呼叫該 Web 服務。

如何透過 .NET 呼叫以 SOAP 為主的 Web Service

其實你要撰寫 SOAP 的 Client 端,透過 Visual Studio 2022 其實蠻容易的,大致步驟如下:

  1. 開啟 Connected Service 視窗

    image

  2. 點擊 Add a service reference

    image

  3. 選擇 WCF Web Service

    image

  4. 輸入 URI ( e 政府服務平臺的單一登入機制的 Web Service 網址為 https://www.cp.gov.tw/gsp2ws/rsmediator01.asmx )

    image

  5. 查看 Web Service 內容,並按下 Next 進入下一步

    image

  6. 按下 Next 進入下一步

    image

  7. 按下 Finish 完成

    image

  8. 按下 Close 關閉對話框

    image

  9. 完成後會自動產生一個 Connected Services\ServiceReference1\Reference.cs 檔案

    image

如果不用 Visual Studio 2022 的話,就要透過 dotnet-svcutil 全域工具來快速產生程式碼:

  1. 安裝全域工具

    dotnet tool install --global dotnet-svcutil
    
  2. 產生參考類別

    dotnet svcutil https://www.cp.gov.tw/gsp2ws/rsmediator01.asmx
    

    這個命令會產生 ServiceReference\Reference.cs 程式碼內容,其內容跟 Visual Studio 2022 產生的程式碼一樣。

  3. ServiceReference\Reference.cs 程式碼複製到你的專案下即可!

當我們擁有了 SOAP 的 Client 函式庫,剩下的就很簡單了,要呼叫 Web Service 只需要以下幾個步驟:

  1. 建立 Web Service 的 Client 實例

    var client = new GSP2_RS_Service_01SoapClient(GSP2_RS_Service_01SoapClient.EndpointConfiguration.GSP2_RS_Service_01Soap);
    
  2. 呼叫並傳入 AuthHeader 參數

    var result = await client.GetProfileColumns2Async(new AuthHeader()
    {
        ServiceID = "APP0000001",
        Token1 = "7c2232c2ca3a4a002fc4127d4c2ca8a9"
    });
    
  3. 判斷是否有成功取得使用者基本資料

    if (result.GetProfileColumns2Result.Code == 0)
    {
        Console.WriteLine("成功取得個人資料");
    }
    

相關連結

留言評論