簡體   English   中英

反應和 reCAPTCHA v3

[英]React and reCAPTCHA v3

有什么簡單的方法可以在反應中使用 reCAPTCHA v3 嗎? 谷歌搜索是否只能找到 v2 的組件。 並且只有 react-recaptcha-v3 用於 v3。

但是當我嘗試使用該組件時,出現錯誤 Invalid site key or not loaded in api.js。

嘿,你不需要包裹,它只是一個你不需要的不必要的包裹。 https://medium.com/@alexjamesdunlop/unnecessary-packages-b3623219d86我寫了一篇關於為什么你不應該使用它和另一個包的文章。 不要依賴某些包:而是依賴谷歌 :)

const handleLoaded = _ => {
  window.grecaptcha.ready(_ => {
    window.grecaptcha
      .execute("_reCAPTCHA_site_key_", { action: "homepage" })
      .then(token => {
        // ...
      })
  })
}

useEffect(() => {
  // Add reCaptcha
  const script = document.createElement("script")
  script.src = "https://www.google.com/recaptcha/api.js?render=_reCAPTCHA_site_key"
  script.addEventListener("load", handleLoaded)
  document.body.appendChild(script)
}, [])

return (
  <div
    className="g-recaptcha"
    data-sitekey="_reCAPTCHA_site_key_"
    data-size="invisible"
  ></div>
)

我正在自學 React + TypeScript,這就是我想出的實現 recaptcha v3 的方法。

我想要一個簡單的解決方案,可以讓我:

  • 僅在提交表單時動態獲取令牌,以避免超時和重復令牌錯誤
  • 出於隱私原因(例如登錄、注冊、忘記密碼)僅在某些組件上使用 recaptcha,而不是在 index.html 中全局定義 recaptcha api.js
  • 需要盡可能少的代碼在組件中實現

reCAPTCHA.ts

declare global {
    interface Window {
        grecaptcha: any;
    }
}

export default class reCAPTCHA {
    siteKey: string;
    action: string;

    constructor(siteKey: string, action: string) {
        loadReCaptcha(siteKey);
        this.siteKey = siteKey;
        this.action = action;
    }

    async getToken(): Promise<string> {
        let token = "";
        await window.grecaptcha.execute(this.siteKey, {action: this.action})
            .then((res: string) => {
                token = res;
            })
        return token;
    }
}

const loadReCaptcha = (siteKey: string) => {
    const script = document.createElement('script')
    script.src = `https://www.recaptcha.net/recaptcha/api.js?render=${siteKey}`
    document.body.appendChild(script)
}

要使用此類,請將其聲明為組件中的屬性:

recaptcha = new reCAPTCHA((process.env.REACT_APP_RECAPTCHA_SITE_KEY!), "login");

在表單提交上獲取您需要傳遞給后端的令牌:

let token: string = await this.recaptcha.getToken();

在后端驗證令牌:

重新驗證.ts

const fetch = require("node-fetch");
const threshold = 0.6;

export async function validateRecaptcha(recaptchaToken: string, expectedAction: string) : Promise<boolean> {
    const recaptchaSecret = process.env.RECAPTCHA_SECRET_KEY;
    const url = `https://www.recaptcha.net/recaptcha/api/siteverify?secret=${recaptchaSecret}&response=${recaptchaToken}`;
    let valid = false;
    await fetch(url, {method: 'post'})
        .then((response: { json: () => any; }) => response.json())
        .then((data: any)=> {
            valid = (data.success && data.score && data.action && data.score >= threshold && data.action === expectedAction);
        });
    return valid;
}

我對 JS/TS 和 React 的經驗非常有限,但這個解決方案對我有用。 我歡迎任何關於改進此代碼的意見。

您可以使用react-google-recaptcha3 npm 包(大小:~5 KB)

npm i react-google-recaptcha3

用法

import ReactRecaptcha3 from 'react-google-recaptcha3';

const YOUR_SITE_KEY = '';

function App() {
 // load google recaptcha3 script
  useEffect(() => {
    ReactRecaptcha3.init(YOUR_SITE_KEY).then(
      (status) => {
        console.log(status);
      }
    );
  }, [])

}

現在在表單提交上,您需要生成令牌,然后將其附加到您的表單數據中

  const submit = () => {
    const formData = { name: "John", lastname: "Doe" }
    ReactRecaptcha3.getToken().then(
      (token) => {
        console.log(token);
        formData.token = token;
        // send request to backend
        fetch(url, { method: 'POST', body: JSON.stringify(formData) }).then(...)
        
      },
      (error) => {
        console.log(error);
      }
    );
  };

現在在后端你需要驗證令牌

const request = require('request-promise');

const secretKey = YOUR_RECAPTCHA_SECRET_KEY;
const userIp = 'USER_IP';
request.get({
    url: `https://www.google.com/recaptcha/api/siteverify?secret=${secretKey}&response=${recaptchaToken}&remoteip=${userIp}`,
}).then((response) => {

    // If response false return error message
    if (response.success === false) {
        return res.json({
            success: false,
            error: 'Recaptcha token validation failed'
        });
    }
    // otherwise continue handling/saving form data
    next();
})

Stackblitz 示例

試試這個! https://github.com/t49tran/react-google-recaptcha-v3 npm install react-google-recaptcha-v3

您還可以使用 React (Typescript) 創建自己的自定義鈎子useReCaptcha

// hooks/useReCaptcha.ts

import { RECAPTCHA_KEY, RECAPTCHA_TOKEN } from 'config/config'
import { useEffect, useState } from 'react'

const showBadge = () => {
  if (!window.grecaptcha) return
  window.grecaptcha.ready(() => {
    const badge = document.getElementsByClassName('grecaptcha-badge')[0] as HTMLElement
    if (!badge) return
    badge.style.display = 'block'
    badge.style.zIndex = '1'
  })
}

const hideBadge = () => {
  if (!window.grecaptcha) return
  window.grecaptcha.ready(() => {
    const badge = document.getElementsByClassName('grecaptcha-badge')[0] as HTMLElement
    if (!badge) return
    badge.style.display = 'none'
  })
}

const useReCaptcha = (): { reCaptchaLoaded: boolean; generateReCaptchaToken: (action: string) => Promise<string> } => {
  const [reCaptchaLoaded, setReCaptchaLoaded] = useState(false)

  // Load ReCaptcha script
  useEffect(() => {
    if (typeof window === 'undefined' || reCaptchaLoaded) return
    if (window.grecaptcha) {
      showBadge()
      setReCaptchaLoaded(true)
      return
    }
    const script = document.createElement('script')
    script.async = true
    script.src = `https://www.google.com/recaptcha/api.js?render=${RECAPTCHA_KEY}`
    script.addEventListener('load', () => {
      setReCaptchaLoaded(true)
      showBadge()
    })
    document.body.appendChild(script)
  }, [reCaptchaLoaded])

  // Hide badge when unmount
  useEffect(() => hideBadge, [])

  // Get token
  const generateReCaptchaToken = (action: string): Promise<string> => {
    return new Promise((resolve, reject) => {
      if (!reCaptchaLoaded) return reject(new Error('ReCaptcha not loaded'))
      if (typeof window === 'undefined' || !window.grecaptcha) {
        setReCaptchaLoaded(false)
        return reject(new Error('ReCaptcha not loaded'))
      }
      window.grecaptcha.ready(() => {
        window.grecaptcha.execute(RECAPTCHA_KEY, { action }).then((token: string) => {
          localStorage.setItem(RECAPTCHA_TOKEN, token)
          resolve(token)
        })
      })
    })
  }

  return { reCaptchaLoaded, generateReCaptchaToken }
}

export default useReCaptcha

然后在登錄組件中,例如,你可以調用這個自定義鈎子:

// Login.ts

import React from 'react'
import useReCaptcha from 'hooks/useReCaptcha'

const LoginPageEmail = () => {
  const { reCaptchaLoaded, generateReCaptchaToken } = useReCaptcha()

  const login = async () => {
    await generateReCaptchaToken('login') // this will create a new token in the localStorage
    await callBackendToLogin() // get the token from the localStorage and pass this token to the backend (in the cookies or headers or parameter..)
  }

  return (
    <button disabled={!reCaptchaLoaded} onClick={login}>
      Login
    </button>
  )
}

export default LoginPageEmail

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM