【reCAPTCHA】Google reCAPTCHA v3を実装してみる【Spring Boot】

spring boot

Google reCAPTCHA v3は、ウェブサイトがボットからの不正なアクセスを防ぐためのサービスです。以前のバージョン(v1やv2)と異なり、reCAPTCHA v3はユーザーに対して「私はロボットではありません」チェックボックスや画像認証を求めることなく、ユーザーの行動をスコアリングして自動的にボットかどうかを判断します。

まずはGoogle reCAPTCHAのサイトで登録を行い、サイトキー、シークレットキーを取得します。

今回はローカル環境で確認するので、ドメインはlocalhostと設定しています。

■送信画面(一部抜粋)

<script src="https://www.google.com/recaptcha/api.js?render=取得したサイトキー"></script>
<form th:action="@{/account/register}" id="accountForm" th:object="${account}" method="post">
    <div>
        <label for="username">Username</label>
        <input type="text" id="username" th:field="*{username}" required/>
    </div>
    <div>
        <label for="password">Password</label>
        <input type="password" id="password" th:field="*{password}" required/>
    </div>
    <div>
        <button type="button" id="accountSubmitBtn">Register</button>
    </div>
    <input type="hidden" name="recaptchaResponse" id="recaptchaResponse">
</form>

ポイントは、recaptchaのscriptを読み込みます。rendarには上記で取得したサイトキーを指定。

<script src="https://www.google.com/recaptcha/api.js?render=取得したサイトキー"></script>

recaptchaのtoken設定用のhidden要素を追加します。

<input type="hidden" name="recaptchaResponse" id="recaptchaResponse">

■javascript(一部抜粋)
submit時に用意したhiddenにtokenを設定するだけ。
ここでもgrecaptcha.executeの引数にサイトキーを指定する。

 $("#accountSubmitBtn").click(function() {
        // reCAPTCHA v3用
        grecaptcha.ready(function () {
            grecaptcha.execute('取得したサイトキー').then(function(token) {
                $('#recaptchaResponse').val(token);
                $("#accountForm").submit();
            });
        });
	});

■Controllerクラス(一部抜粋)

recaptchaの結果を判定して、ハンドリングを行っています。
今回はscoreが0.8以上が人間による操作と判定させています。

@RequestMapping("account")
@Controller
@RequiredArgsConstructor
@Slf4j
public class AccountController {

    private final UserService userService;
    private final RecaptchaV3Service recaptchaService;

    @PostMapping("/register")
    public String registerUser(@Valid @ModelAttribute("account") AccountForm form, Model model) {
    	RecaptchaResult result = recaptchaService.verify(form.getRecaptchaResponse());
    	log.info("reCAPTCHA result: " + result.toString());
        if (result.isSuccess() && result.getScore() >= 0.8) {
       // ユーザ登録処理
            userService.registerUser(form, passwordEncoder);  
       // 登録成功後にログインページにリダイレクト
            return "redirect:/login";  
        } else {
        	  //recaptcha失敗
            return "redirect:/register";
        }
    }
}

■Serviceクラス(一部抜粋)

public class RecaptchaV3Service {

    @Value("${recaptcha.secret}")
    private String recaptchaSecret;

    private static final String RECAPTCHA_VERIFY_URL = "https://www.google.com/recaptcha/api/siteverify";

    public RecaptchaResult verify(String recaptchaResponse) {
        RestTemplate restTemplate = new RestTemplate();
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(RECAPTCHA_VERIFY_URL)
                .queryParam("secret", recaptchaSecret)
                .queryParam("response", recaptchaResponse);
        return restTemplate.postForObject(builder.toUriString(), null, RecaptchaResult.class);
    }
}

■recaptcha応答結果クラス

recaptchaからの結果を受け取るためのクラス
詳細はこちらを参照

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class RecaptchaResult {
    private boolean success;
    private float score;
    private String action;
    private String challenge_ts;
    private String hostname;
    public RecaptchaResult() {
    }
}

Formクラス

@Data
public class AccountForm {
	@NotBlank(message = "Usernameは必須です")
	private  String username;
	@NotBlank(message = "パスワードは必須です")
	private  String password;
	private String recaptchaResponse;
}

シークレットキーをプロパティファイルに設定

recaptcha:
  secret: 取得したシークレットキー

入力画面を確認すると、recaptchaが確認できます。

実行ログでサブミット後のscoreを確認すると0.9でした。
scoreは0〜1の間で1に近いほど人間が操作、0に近いほどロボットが操作ということです。

reCAPTCHA result: RecaptchaResult(success=true, score=0.9, action=null, challenge_ts=2024-07-06T01:04:08Z, hostname=localhost)

管理画面から、不正なリクエストがどれくらいあったかを確認することができます。

タイトルとURLをコピーしました