使用 Google验证码 ReCaptcha V3

使用 Google验证码 ReCaptcha V3

验证码的作用,不必多说。而且,谷歌的验证码想必多多少少,大家都见过

这是v2版本的验证码,需要用户去主动点击【我不是机器人】摁钮来触发验证。

image

在新的v3版本中,已经不需要这些东西了。整个验证过程对于用户来说,是无感的。

官方文档
https://developers.google.com/recaptcha/intro

管理后台
https://www.google.com/recaptcha/admin/site/350006937

注册应用

需要科学上网

填写资料

https://www.google.com/recaptcha/admin/create

标签,随便填写,类似于名称。
reCaptcha类型,选择第3版
域名,填写web站点的域名

注册成功后,得到密钥

2个密钥,一个是在客户端(HTML)使用,一个是在服务端使用

前端

官方的demo

<script src="https://www.google.com/recaptcha/api.js?render=_reCAPTCHA_site_key"></script>
<script>
	grecaptcha.ready(function() {
		grecaptcha.execute('_reCAPTCHA_site_key_', {action: 'homepage'}).then(function(token) {
		
		});
	});
</script>

_reCAPTCHA_site_key_ 就是客户的的密钥

核心代码就是通过执行 grecaptcha.execute(...) 方法,在回调中获取到 token值。

action 参数 可以理解为 验证场景。可以自己定义名称。作用就是可以在后台,对于不同的场景进行特别的设置。例如专门为登录设计一个场景: login

后端的验证

请求接口 https://www.google.com/recaptcha/api/siteverify
请求方法 POST

POST 参数 描述
secret 必须的,服务端的密钥
response 必须的,客户端验证的Token.
remoteip 可选的,客户端的ip地址

响应

{
  score: 0.9  // 评分0 到 1。1:确认为人类,0:确认为机器人。
  hostname: "localhost"  // 请求的地址
  success: true  // 是否验证成功,
  challenge_ts: "2020-02-27T05:26:05Z"
  action: "homepage"
}

系统可以根据 score 值来判定此次请求是否合法。

国内的用户需要替换js文件的地址和服务端验证接口的地址(Fuck GFW)

替换客户端js地址

https://www.google.com/recaptcha/api.js
替换为
https://www.recaptcha.net/recaptcha/api.js

替换服务端接口地址

https://www.google.com/recaptcha/api/siteverify
替换为
https://www.recaptcha.net/recaptcha/api/siteverify

完整的一个例子

客户端

在模板引擎中,填充客户的的密钥: ${captchaClientSecret}

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>谷歌ReCaptcha</title>
	</head>
	<body>   
		<button>点击我执行验证</button>
	
	<script src="https://www.recaptcha.net/recaptcha/api.js?render=${captchaClientSecret}"></script>
	<script type="text/javascript">
		const CAPTCHA_CLIENT_SECRET = "${captchaClientSecret}";
		window.onload = () => {
			document.querySelector('button').addEventListener('click', () => {
				grecaptcha.execute(CAPTCHA_CLIENT_SECRET, {action: 'homepage'}).then(function(token) {
					console.log('客户端token:' + token);
					fetch('/validate?token=' + token, {
						method: 'GET'
					}).then(response => {
						if (response.ok){
							response.json().then(message => {
								console.log('服务端验证');
								console.log(message);
							});
						}
					});
				});
			});
		};
	</script>   
	</body>  
</html>

服务端

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.alibaba.fastjson.JSONObject;

@RestController
@RequestMapping("/validate")
public class ValidateController {
	
	@Value("${google.recaptcha.validate-api}")
	private String validateApi;
	
	@Value("${google.recaptcha.server-secret}")
	private String captchaServerSecret;
	
	@Autowired
	RestTemplate restTemplate;
	
	@GetMapping
	public Object validate (HttpServletRequest request,
						@RequestParam("token")String token) {
		
		HttpHeaders httpHeaders = new HttpHeaders();
		httpHeaders.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);

		MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap<>();
		requestBody.add("secret", this.captchaServerSecret);
		requestBody.add("response", token);
		requestBody.add("remoteip", request.getRemoteAddr()); // 客户的ip地址,不是必须的参数。
		
		ResponseEntity<JSONObject> responseEntity = restTemplate.postForEntity(this.validateApi, new HttpEntity<>(requestBody,httpHeaders), JSONObject.class);
		
		if (!responseEntity.getStatusCode().is2xxSuccessful()) {
			// TODO 异常的HTTP状态码
		}
		
		return responseEntity.getBody();
	}
}

隐藏ReCAPTCHA图标

使用 reCAPTCHA,会在网站上提示出一个图标.。
image

如果需要隐藏,可以添加css

.grecaptcha-badge { 
	display: none; 
} 

image

基于K哥 上面的服务端代码 直接用就完事了

v2 版本 html 代码

其中 sitekey 为 你申请的v2版本的html 秘钥

食用方法同v3版本

<!DOCTYPE html>
<html>
<head>
    <title>google验证码V2</title>
    <meta charset="UTF-8">
</head>
<body>
    <div id="google-reCaptcha"></div>
    <button>验证后提交</button>
</body>
<script src="https://www.recaptcha.net/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer> </script>
<script type="text/javascript">
    var verifyCallback = function(token) {
        document.querySelector('button').addEventListener('click', () => {
            console.log('客户端token:' + token);
        fetch('/validate?token=' + token, {
            method: 'GET'
        }).then(response => {
            if (response.ok){
            response.json().then(message => {
                console.log('服务端验证');
            console.log(message);
            alert(message);
        });
        }
    });
    });

    };
    var onloadCallback = function() {
        grecaptcha.render('google-reCaptcha', {
            'sitekey' : '6LdctdwUAAAAAORpZqPoqPta7PUKjU4Wl24JIGRs',
            'callback' : verifyCallback,
            'theme' : 'light'
        });
    };
</script>
</html>

1 Like

写了个ReCaptcha v2的demo,后端用Python的Flask实现

http://recaptcha.springboot.io:8080/