[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-理解與前端實作-recaptcha-v3":3},{"id":4,"slug":5,"title":6,"description":7,"content":8,"date":9,"image":10,"imageAlt":6,"author":11,"categories":14,"path":20,"minRead":21},3786,"理解與前端實作-recaptcha-v3","理解與前端實作 reCAPTCHA v3","理解與實作 recaptcha Google reCAPTCHA 是一種常見的機器人驗證技術，用來區分人類使用 […]","\u003Ch1>理解與實作 recaptcha\u003C\u002Fh1>\n\u003Cp>Google reCAPTCHA  是一種常見的機器人驗證技術，用來區分人類使用者和自動化程式。現在主流有 v2 和 v3 兩種版本，是擁有免費額度的付費服務。\u003Cbr \u002F>\n先前都只有透過打包好的 WordPress 外掛來實作，這次有機會在Nuxt3的前端專案中自己寫，就趁機記錄一下自己的理解與過程。\u003C\u002Fp>\n\u003Cp>本次使用的是 reCAPTCHA v3 ，特點在於不用跟介面互動就能取得辨識結果，好處就是網站對使用者的驗證只會在背景處理，不影響使用體驗。\u003C\u002Fp>\n\u003Cp>reCAPTCHA v3的技術原理大概可以這樣理解：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>行為分析：評估使用者與頁面的互動方式，如滑鼠移動模式、點擊方式和速度。\u003C\u002Fli>\n\u003Cli>瀏覽器指紋識別：收集瀏覽器和設備資訊，如作業系統、瀏覽器類型、螢幕解析度等。\u003C\u002Fli>\n\u003Cli>Cookie 和歷史數據：分析用戶之前與 Google 服務的互動歷史。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>實作邏輯的話，其實就是跟 reCAPTCHA 服務取用資料這樣。\u003C\u002Fp>\n\u003Ch3>前置：取得金鑰\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>網站金鑰 (Site Key)：公開的，嵌入在網頁前端代碼中\u003C\u002Fli>\n\u003Cli>密鑰 (Secret Key)：私密的，只在伺服器端使用\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>實作流程\u003C\u002Fh3>\n\u003Cp>以下是我對  reCAPTCHA 溝通的理解：\u003C\u002Fp>\n\u003Ch4>1. 前端向 Google reCAPTCHA 請求 token\u003C\u002Fh4>\n\u003Cp>首先註冊並申請 Google reCAPTCHA 服務。\u003Cbr \u002F>\n網站前端載入 reCAPTCHA SDK，當使用者執行需驗證的操作時，呼叫grecaptcha.execute()， reCAPTCHA 服務會分析使用者行為後回傳 token。\u003C\u002Fp>\n\u003Ch4>2. 前端向網站伺服器提交請求\u003C\u002Fh4>\n\u003Cp>前端將 reCAPTCHA token 連同表單數據或 API 請求一起發送給後端。\u003C\u002Fp>\n\u003Ch4>3. 伺服器向 Google reCAPTCHA 驗證 token\u003C\u002Fh4>\n\u003Cp>後端使用密鑰(secret key)向 Google reCAPTCHA API 發送驗證請求。\u003C\u002Fp>\n\u003Ch4>4. Google reCAPTCHA 返回驗證結果\u003C\u002Fh4>\n\u003Cp>JSON 格式的回應包含:\u003Cbr \u002F>\nsuccess: 驗證是否成功(true\u002Ffalse)\u003Cbr \u002F>\nscore: 闕值，人類可能性分數(0.0-1.0)\u003Cbr \u002F>\naction: 觸發驗證的行為名稱\u003Cbr \u002F>\nchallenge_ts: 時間戳記\u003Cbr \u002F>\nhostname: 網站域名\u003C\u002Fp>\n\u003Ch3>閾值判斷：\u003C\u002Fh3>\n\u003Cp>我這次使用的 reCAPTCHA v3 會回傳 0.0 到 1.0 之間的分數，此分數表示使用者行為的「人類可能性」，管理者可自行決定接受的閾值（例如 0.5）高於閾值就允許後續操作，低於則可能需要額外驗證或拒絕請求。\u003C\u002Fp>\n\u003Cp>\u003Ca href=\"https:\u002F\u002Fpost.billxu.net\u002Fwp-content\u002Fuploads\u002F2025\u002F04\u002Fdiagram.svg\">\u003Cimg decoding=\"async\" src=\"\u002Fapi\u002Fproxy-image?url=https%3A%2F%2Fpost.billxu.net%2Fwp-content%2Fuploads%2F2025%2F04%2Fdiagram.svg\" alt=\"\" \u002F>\u003C\u002Fa>\u003C\u002Fp>\n\u003Ch2>實作紀錄：\u003C\u002Fh2>\n\u003Cp>我使用 vue-recaptcha-v3 ，自行建立 plugin 管理與啟動 reCAPTCHA，因為只有前端實作，所以很快就處理好了。\u003C\u002Fp>\n\u003Cp>recaptcha.client.js\u003C\u002Fp>\n\u003Cpre>\u003Ccode>import { VueReCaptcha } from &#039;vue-recaptcha-v3&#039;\n\nexport default defineNuxtPlugin(async (nuxtApp) =&gt; {\n  const config = useRuntimeConfig()\n\n  \u002F\u002F 確保 siteKey 存在並輸出調試信息\n  console.log(&#039;Initializing reCAPTCHA with config:&#039;, {\n    siteKey: config.public.recaptchaSiteKey,\n    isDevelopment: process.env.NODE_ENV === &#039;development&#039;\n  })\n\n  if (!config.public.recaptchaSiteKey) {\n    console.error(&#039;reCAPTCHA site key is missing&#039;)\n    return\n  }\n\n  try {\n    await nuxtApp.vueApp.use(VueReCaptcha, {\n      siteKey: config.public.recaptchaSiteKey,\n      loaderOptions: {\n        useRecaptchaNet: true, \u002F\u002F 從 recaptcha.net 載入 script\n        autoHideBadge: false,\u002F\u002F 隱藏標籤\n        explicitRenderParameters: {\n          badge: &#039;bottomright&#039;,\n          size: &#039;invisible&#039;\n        }\n      }\n    })\n\n    \u002F\u002F 驗證 reCAPTCHA 是否正確載入\n    const recaptcha = await window.grecaptcha\n    if (!recaptcha) {\n      throw new Error(&#039;reCAPTCHA not loaded properly&#039;)\n    }\n\n    console.log(&#039;reCAPTCHA plugin initialized successfully&#039;)\n  } catch (error) {\n    console.error(&#039;Failed to initialize reCAPTCHA:&#039;, error)\n  }\n}) \u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>然後在 .env 和 nuxt.config 放好 API_KEY\u003C\u002Fp>\n\u003Cp>例如在登入時就宣告上述的流程即可\u003C\u002Fp>\n\u003Cpre>\u003Ccode>import { useReCaptcha } from &#039;vue-recaptcha-v3&#039;\nconst { executeRecaptcha, recaptchaLoaded } = useReCaptcha()\n\nconst handleLogin = async (e) =&gt; {\n\n\u002F\u002F 執行 reCAPTCHA 驗證\nconsole.log(&#039;開始執行 reCAPTCHA 驗證&#039;)\nconst recaptchaToken = await executeRecaptcha(&#039;login&#039;)\nconsole.log(&#039;recaptchaToken:&#039;, recaptchaToken)\n\n\u002F\u002F 發送登入請求\nconst response = await $fetch(`\u002Fapi\u002Flogin`, {\n    method: &#039;POST&#039;,\n        body: {\n        account: account.value,\n        password: password.value,\n        recaptchaToken: recaptchaToken\n        }\n    })\nif (response) {\n\u002F\u002F後端回傳闕值做後續處理\n}\n\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>後端就要另外寫一隻配合密鑰的請求去詢問結果。\u003C\u002Fp>\n\u003Cp>成功載入的話，網站會出現 reCAPTCHA 的標籤，預設在右下方。\u003C\u002Fp>\n","2025-04-02T12:29:05","\u002Fapi\u002Fproxy-image?url=https%3A%2F%2Fpost.billxu.net%2Fwp-content%2Fuploads%2F2024%2F09%2FNuxt_logo.svg.png",{"name":12,"link":13},"Chih Fan","http:\u002F\u002Fpost.billxu.net",[15,17],{"name":16,"slug":16},"frontend",{"name":18,"slug":19},"Nuxt","nuxt","\u002Fblog\u002F理解與前端實作-recaptcha-v3",2]