[JavaScript] reCAPTCHAのサーバー認証処理をメールプログラムに手を入れず行いたい
- メールフォームの迷惑メール対策の方法にGoogle reCAPTCHA(リキャプチャ)があります。「私はロボットではありません」でお馴染みのあのチェックボックスです
- Googleから提供されるAPIを利用することで、メールフォームに認証機能を持たせることができます
- しかし、設置にはサーバー側のメール送信プログラムに手を加える必要があり、プラグインや外部のメールフォームを呼び出している場合、おいそれと組み込むことができません
- そこで今回は、Ajax(XMLHttpRequest)を利用することでサーバーサイドのプログラムに手を加えず※にreCAPTCHAを実装する方法をご紹介します
- ※厳密に言うと、サーバーサイドに一つphpを用意することになります
Google reCAPTCHAからキーを取得
- こちらからreCAPTCHAの利用を開始し、サイトキーとシークレットキーを入手してください
- 利用開始までの手続きはそれほど複雑ではないため、詳しい説明は割愛します
サンプルコード
html
<form name="gr_form">
<div id="gr_checkbox"></div>
<input type="button" value="送信" disabled="disabled" id="gr_submit" onclick="trySubmit();" />
</form>
- 適宜、フォームの中にinputタグなどを追加してください
- 「gr_checkbox」のIDをつけた要素が「私はロボットではありません」のチェックボックスになります
- フォームにはあえてaction属性を指定していません。認証が完了した際、JavaScriptで動的にaction属性を設定します
- 送信ボタンには「disabled」属性を設定して無効状態にしています。後述するJavaScriptの処理で、「私はロボットでは〜」にチェックがされたとき「disabled」を解除します
CSS
#gr_checkbox {
display: table;
margin: 10px auto;
}
#gr_submit:disabled {
pointer-events: none;
opacity: 0.5;
}
- 「#gr_submit」は送信ボタンに設定したIDです。送信ボタンがdisabledのとき、操作を禁止してボタンを透過します
JavaScript
<script src="https://www.google.com/recaptcha/api.js?onload=grOnloadCallback&render=explicit" async defer></script>
<script type="text/javascript">
var grOnloadCallback = function() {
grecaptcha.render('gr_checkbox', {
'sitekey' : 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'callback' : grCallback,
'expired-callback' : grExpiredCallback
});
};
var grCallback = function(response) {
document.getElementById('gr_submit').removeAttribute('disabled');
};
var grExpiredCallback = function(response) {
document.getElementById('gr_submit').setAttribute('disabled', 'disabled');
};
var trySubmit = function() {
if( document.gr_form.reportValidity() ) {
var xhr = new XMLHttpRequest();
xhr.open('POST', '/gr_check.php', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status === 200) {
if('' != xhr.responseText) {
document.gr_form.action = xhr.responseText;
document.gr_form.method = 'POST';
document.gr_form.submit();
}
}
}
var gr_response = document.getElementById('g-recaptcha-response').value;
xhr.send('response=' + gr_response);
}
}
</script>
- JavaScriptの処理は大まかに2つ。「私はロボットでは〜」のチェックボックス関連の処理と、送信ボタンを押したときのサーバーサイドの認証処理となります
- 以下、部分的に抜粋して解説します
var grOnloadCallback = function() {
grecaptcha.render('gr_checkbox', {
'sitekey' : 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'callback' : grCallback,
'expired-callback' : grExpiredCallback
});
};
- チェックボックス関連の処理です。画面表示時にGoogle側からこの関数が呼ばれ、チェックボックスの描画やチェック時の処理が設定されます
- 「gr_checkbox」がチェックボックスを描画(レンダリング)させたい要素を指すIDになっています
- 「sitekey」には、Googleから取得したサイトキーを設定します
- 「callback」には、チェックボックスがチェックされた後に呼んでもらいたい関数を指定します
- 「expired-callback」には、チェック後のタイムアウト時に呼んでもらいたい関数を指定します。リキャプチャは、一度チェックをした後に2分が経過するとタイムアウトとなり、認証処理がキャンセルされます。タイムアウトしたときに必要な処理を行うためにここで関数を指定します
var grCallback = function(response) {
document.getElementById('gr_submit').removeAttribute('disabled');
};
- チェックボックスにチェックがされたときに呼ばれる関数です
- デフォルトで「disabled」状態だった送信ボタンを有効にして押せるようにします
var grExpiredCallback = function(response) {
document.getElementById('gr_submit').setAttribute('disabled', 'disabled');
};
- チェック後のタイムアウト時に呼ばれる関数です
- チェック時に一度有効化した送信ボタンを再度無効化し、ユーザーがもう一度チェックボックスにチェックをしないと送信できないようにします
var trySubmit = function() {
if( document.gr_form.reportValidity() ) {
var xhr = new XMLHttpRequest();
xhr.open('POST', '/gr_check.php', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status === 200) {
if('' != xhr.responseText) {
document.gr_form.action = xhr.responseText;
document.gr_form.method = 'POST';
document.gr_form.submit();
}
}
}
var gr_response = document.getElementById('g-recaptcha-response').value;
xhr.send('response=' + gr_response);
}
}
- 今回の肝となるサーバーサイドの認証処理になります
- リキャプチャは、チェックボックスによる認証とGoogle側へのリクエスト認証の二重のチェック処理で実現します
- チェックボックスにチェックを入れると、Googleからレスポンスキーを受け取ります。このレスポンスキーと、シークレットキーをGoogleの認証サーバーにリクエスト送信することで、きちんとチェックボックスの操作を経た送信であることを保証します
- 本来は、このリクエスト処理をメールプログラム内に組み込むことになりますが、今回はリクエスト処理をJavaScriptで行うことで、サーバーサイドのメールプログラムに手を入れずに済むようにしています
- リクエスト処理は「XMLHttpRequest」を使用し、Googleと認証のやり取りを行う「gr_check.php」を呼び出します
- 認証に成功したとき「gr_check.php」からのレスポンスとして、実行するメールプログラムのパスを受け取ります。これをフォームのaction属性に設定し「document.gr_form.submit();」で送信を行います
gr_check.php
<?php
$token = $_POST['response'];
if($token) {
$url = 'https://www.google.com/recaptcha/api/siteverify';
$secret_key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$response = file_get_contents("{$url}?secret={$secret_key}&response={$token}");
$json = json_decode($response, true);
if($json['success'] == 'true') {
echo 'https://xxx.com/mail.cgi';
}
}
?>
- Googleへのリクエストを行うサーバーサイドの処理です
- 「secret_key」にはGoogleから取得したシークレットキーを設定します
- シークレットキーと、JavaScript側からの「XMLHttpRequest」で渡されてきたレスポンスキーを使い、「file_get_contents」でGoogleにリクエストを行います
- リクエストの結果が成功であるなら、メールプログラムの実行パスをechoします。これがJavaScript側へのレスポンスとなります。
ご質問など受け付けています
記事の中でわかりにくかったところ、もっと知りたかったこと、間違っていることなど、何でもお気軽にご連絡ください。
ご連絡は下記フォームを利用いただくか、ツイッターアカウント@flat8migi宛てでもOKです。