用PHP搭配Cloudflare Turnstile CAPTCHA
網路上有許多機器人驗證的替代方案,這裡介紹與 Google reCAPTCHA 寫法幾乎一樣的 Turnstile 給大家嘗試看看。
下面的寫法給各位參考:
後端PHP傳送與接收資料
與 Google 一樣,將query「response」與「secret」丟過去就可以了(範例多了「remoteip」,這個不一定要有)。
// captcha.php
$captcha = $_POST['cf-turnstile-response'];
$secretKey = 'your_secret_key';
$ip = $_SERVER['REMOTE_ADDR'];
$url_path = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
$data = array('secret' => $secretKey, 'response' => $captcha, 'remoteip' => $ip);
$opts = array(
'http'=> array(
'method'=> 'POST',
'content'=> http_build_query($data)
)
);
$query = stream_context_create($opts);
$result = file_get_contents($url_path, false, $query);
echo json_encode($result);
// 如果你想在後端直接判斷有沒有成功
$array = json_decode($result, true);
$success = $array['success'];
if($success){
// 驗證成功
}else{
// 驗證失敗
}
後端PHP這樣寫沒有問題,但資料回傳到前端JavaScript問題就出現了。
前端JavaScript接收Object
$.ajax({
type: 'POST',
url: 'captcha.php',
data: {
'cf-turnstile-response': $('[name="cf-turnstile-response"]').val()
},
}).done(function(data) {
data = JSON.parse(JSON.parse(data).toString());
if(!data.success){
alert(data['error-codes'].toString());
}
})
你會發現我 JSON.parse 了兩次,還 toString() 了一次,為什麼?我們來看正常情況只 JSON.parse 一次會發生什麼事:
// ...
data = JSON.parse(data);
console.log(data);
// {"success":false,"error-codes":["missing-input-response"],"messages":[]}
console.log(data.success);
// undefined
console.log(data[0]);
// '{'
console.log(Object.keys(data).map(k => data[k]));
console.log(Object.values(data));
// 兩種寫法得到的結果一樣
// ['{', '"', 's', 'u', 'c', 'c', 'e', 's', 's', '"', ':', 'f', 'a', 'l', 's', 'e', ',', '"', 'e', 'r', 'r', 'o', 'r', '-', 'c', 'o', 'd', 'e', 's', '"', ':', '[', '"', 'm', 'i', 's', 's', 'i', 'n', 'g', '-', 'i', 'n', 'p', 'u', 't', '-', 'r', 'e', 's', 'p', 'o', 'n', 's', 'e', '"', ']', ',', '"', 'm', 'e', 's', 's', 'a', 'g', 'e', 's', '"', ':', '[', ']', '}']
一開始的 data 看似正常,但是在用 key 去取得 value 的時候卻是這副德行,解決方法就是先 toString() 之後再 JSON.parse 一次。
另一種差不多的寫法
這次我們先在PHP建立回傳前端專用的array,並且在驗證完之後先不要json_encode,回傳array的時候再json_encode:
$message = array();
// ...
$result = file_get_contents($url_path, false, $query);
$message['captcha'] = $result;
echo json_encode($message, true);
JavaScript的寫法就變得單純許多:
// ...
data = JSON.parse(data);
let captcha = JSON.parse(data.captcha);
if(!captcha.success){
alert(captcha['error-codes'].toString());
turnstile.reset();
}
希望這些資訊多少有幫助到你。
留言
張貼留言