.Net Core 3.1 reCAPTCHA Enterprise

  1. 前言
  2. 設定 reCAPTCHA Enterprise
  3. 建立 .Net Core 3.1 專案
  4. 安裝套件
  5. 程式實作
  6. 測試結果
  7. 補充

前言

最近公司計畫導入 reCAPTCHA Enterprise,本篇文章 記錄一下實作細節

設定 reCAPTCHA Enterprise

# 建立 service-accounts
gcloud iam service-accounts create $Name

# 綁定權限
gcloud projects add-iam-policy-binding $PROJECT_ID --member="serviceAccount:$Name@$PROJECT_ID.iam.gserviceaccount.com" --role="roles/recaptchaenterprise.agent"

# 下載.json檔憑證至本地端 
# 若服務是在GCP上且機器環境的服務帳戶 含有 recaptchaenterprise.assessments.create權限,則可略過 不需憑證
gcloud iam service-accounts keys create $KEY_PATH --iam-account=$NAME@$PROJECT_ID.iam.gserviceaccount.com

ex:
gcloud iam service-accounts create recaptcha-account
gcloud projects add-iam-policy-binding xxx --member="serviceAccount:recaptcha-account@xxx.iam.gserviceaccount.com" --role="roles/recaptchaenterprise.agent"
gcloud iam service-accounts keys create D:\Lab\service-account-file.json --iam-account=recaptcha-account@xxx.iam.gserviceaccount.com  
gcloud recaptcha keys create \
  --web \
  --display-name=[DISPLAY_NAME]  \
  --integration-type=[INTEGRATION_TYPE] \
  --domains=[DOMAINS]

ex:
# 直接取token 驗證分數
gcloud recaptcha keys create \
  --web \
  --display-name=leo-score  \
  --integration-type=SCORE  \
  --domains=localhost

# 透過 checkbox 取token 驗證分數
gcloud recaptcha keys create \
  --web \
  --display-name=leo-checkbox  \
  --integration-type=CHECKBOX  \
  --domains=localhost

設定成功後 目前有兩筆 recaptcha enterprise 金鑰

建立 .Net Core 3.1 專案

dotnet new sln -o reCAPTCHA_Enterprise_Sample
dotnet new mvc -f netcoreapp3.1 -o reCAPTCHA_Enterprise_Sample
dotnet sln reCAPTCHA_Enterprise_Sample/reCAPTCHA_Enterprise_Sample.sln add reCAPTCHA_Enterprise_Sample/reCAPTCHA_Enterprise_Sample.csproj

安裝套件

dotnet add package Google.Cloud.RecaptchaEnterprise.V1

程式實作

呼叫 Google API時,有分在 GCP 的機器環境 跟 地端環境
若服務是跑在GCP的機器環境,需確認機器環境的服務帳戶 含有 recaptchaenterprise.assessments.create 權限 則可以直接呼叫 不需憑證
若服務是在地端環境 或非 GCP 機器環境,則需要將憑證下載至機器上,呼叫時在設定憑證路徑

程式碼可參考 w4560000/reCAPTCHA_Enterprise_Sample

using Google.Api.Gax.ResourceNames;
using Google.Cloud.RecaptchaEnterprise.V1;

namespace reCAPTCHA_Enterprise_Sample.Service
{
    public class CreateAssessmentSample
    {
        public RecaptchaEnterpriseServiceClient GetRecaptchaEnterpriseServiceClient_SetEnv()
        {
            System.Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", @"D:\Lab\reCAPTCHA_Enterprise_Sample\service-account-file.json");
            return RecaptchaEnterpriseServiceClient.Create();
        }

        public RecaptchaEnterpriseServiceClient GetRecaptchaEnterpriseServiceClient_SetCredentialsPath()
        {
            return new RecaptchaEnterpriseServiceClientBuilder()
            {
                CredentialsPath = @"D:\Lab\reCAPTCHA_Enterprise_Sample\service-account-file.json"
            }.Build();
        }

        // Create an assessment to analyze the risk of an UI action.
        // projectID: GCloud Project ID.
        // recaptchaSiteKey: Site key obtained by registering a domain/app to use recaptcha.
        // token: The token obtained from the client on passing the recaptchaSiteKey.
        // recaptchaAction: Action name corresponding to the token.
        public decimal createAssessment(string projectID = "project-id", string recaptchaSiteKey = "recaptcha-site-key",
            string token = "action-token", string recaptchaAction = "action-name")
        {
            // Create the client.
            // TODO: To avoid memory issues, move this client generation outside
            // of this example, and cache it (recommended) or call client.close()
            // before exiting this method.

            // 若在GCP 機器環境的服務帳戶 含有 recaptchaenterprise.assessments.create 權限 則可以直接呼叫 不需憑證
            RecaptchaEnterpriseServiceClient client = RecaptchaEnterpriseServiceClient.Create();

            // 若在地端環境 或 非 GCP 機器環境,則可以將憑證下載至機器上,呼叫時在設定憑證路徑
            //RecaptchaEnterpriseServiceClient client = GetRecaptchaEnterpriseServiceClient_SetEnv(); // 透過環境變數
            //RecaptchaEnterpriseServiceClient client = GetRecaptchaEnterpriseServiceClient_SetCredentialsPath(); // 設定 CredentialsPath

            ProjectName projectName = new ProjectName(projectID);

            // Build the assessment request.
            CreateAssessmentRequest createAssessmentRequest = new CreateAssessmentRequest()
            {
                Assessment = new Assessment()
                {
                    // Set the properties of the event to be tracked.
                    Event = new Event()
                    {
                        SiteKey = recaptchaSiteKey,
                        Token = token,
                        ExpectedAction = recaptchaAction
                    },
                },
                ParentAsProjectName = projectName
            };

            Assessment response = client.CreateAssessment(createAssessmentRequest);

            // Check if the token is valid.
            if (response.TokenProperties.Valid == false)
            {
                System.Console.WriteLine("The CreateAssessment call failed because the token was: " +
                    response.TokenProperties.InvalidReason.ToString());
                return 0;
            }

            // Check if the expected action was executed.
            if (response.TokenProperties.Action != recaptchaAction)
            {
                System.Console.WriteLine("The action attribute in reCAPTCHA tag is: " +
                    response.TokenProperties.Action.ToString());
                System.Console.WriteLine("The action attribute in the reCAPTCHA tag does not " +
                    "match the action you are expecting to score");
                return 0;
            }

            // Get the risk score and the reason(s).
            // For more information on interpreting the assessment,
            // see: https://cloud.google.com/recaptcha-enterprise/docs/interpret-assessment
            System.Console.WriteLine("The reCAPTCHA score is: " + ((decimal)response.RiskAnalysis.Score));

            foreach (RiskAnalysis.Types.ClassificationReason reason in response.RiskAnalysis.Reasons)
            {
                System.Console.WriteLine(reason.ToString());
            }

            return (decimal)response.RiskAnalysis.Score;
        }
    }
}
  • 修改 HomeController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using reCAPTCHA_Enterprise_Sample.Models;
using reCAPTCHA_Enterprise_Sample.Service;
using System;
using System.Diagnostics;

namespace reCAPTCHA_Enterprise_Sample.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;

        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }

        public IActionResult Index()
        {
            TempData["sitekey"] = "6Ld_Os8fAAAAACNQDEOxHKvxBWmN2wK6_GUXAZ08";
            TempData["recaptchaAction"] = "Verify_CheckBox";

            return View();
        }

        public IActionResult Privacy()
        {
            TempData["sitekey"] = "6LeUx88fAAAAAHsA_b3jPKRVg8Yyyua5Ub2qjE8I";
            TempData["recaptchaAction"] = "Verify_GetToken";

            return View();
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }

        [HttpGet]
        public string CreateAssessment(string token, string recaptchaSiteKey, string recaptchaAction)
        {
            try
            {
                // 注意 需自行替換projectID
                return new CreateAssessmentSample().createAssessment(
                            projectID: "xxx",
                            recaptchaSiteKey: recaptchaSiteKey,
                            token: token,
                            recaptchaAction: recaptchaAction).ToString();
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }
    }
}
  • 修改 Index.cshtml
@{
    ViewBag.Title = "Home Page";
}
<head>
    <script src="https://www.google.com/recaptcha/enterprise.js"></script>
</head>
<body>
    <div class="g-recaptcha"
         data-sitekey="@TempData["sitekey"]"
         data-action='@TempData["recaptchaAction"]'
         data-callback='test_recaptcha'>
    </div>
</body>
<script>
    function test_recaptcha(token) {
        $.get(`/Home/CreateAssessment?token=${token}&recaptchaSiteKey=@TempData["sitekey"]&recaptchaAction=@TempData["recaptchaAction"]`)
            .then((res) => alert(res));
    }
</script>
  • 修改 Privacy.cshtml
    @{
      ViewBag.Title = "Home Page";
    }
    @*<style>
          .grecaptcha-badge {
              visibility: hidden;
          }
      </style>*@
    <head>
      <script src="https://www.google.com/recaptcha/enterprise.js?render=@TempData["sitekey"]"></script>
    </head>
    <body>
      <input type="button" class="btn btn-secondary col-sm-2" id="test_verify_score" value="verify_score" onclick="verify_score_click()">
    </body>
    <script>
      function verify_score_click() {
          grecaptcha.enterprise.ready(function () {
              grecaptcha.enterprise.execute('@TempData["sitekey"]', { action: '@TempData["recaptchaAction"]' }).then(function (token) {
                  $.get(`/Home/CreateAssessment?token=${token}&recaptchaSiteKey=@TempData["sitekey"]&recaptchaAction=@TempData["recaptchaAction"]`)
                      .then((res) => alert(res));
              });
          });
      }
    </script>
    

測試結果

  • 測試 Checkbox
    /Home/Index

  • 測試 Score
    /Home/Privacy

補充

若網站右下角 有出現 reCAPTCHA的隱私權說明

可參考 recaptcha 官網 faq 處理方式


轉載請註明來源,若有任何錯誤或表達不清楚的地方,歡迎在下方評論區留言,也可以來信至 leozheng0621@gmail.com
如果文章對您有幫助,歡迎斗內(donate),請我喝杯咖啡

斗內💰

×

歡迎斗內

github