Spring Boot 下使用谷歌 reCAPTCHA v3
Posted sp42a
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot 下使用谷歌 reCAPTCHA v3相关的知识,希望对你有一定的参考价值。
JSP 时代,我写一个图片验证码组件《JSP 实用程序之简易图片验证码》,那是很老的技术,安全性很低,纯粹练手。
图片验证码(Captcha)这类应用,还是采用第三方提供的好,比较放心。于是我想起 Google 的不错,优点如下
- 大厂出品,安全性高
- reCAPTCHA v3 很牛逼,取消了用户互动交互,做到无感知验证
- 免费
原来,我之前第一次用的时候,其实写过博文《免费使用 Google 防注册机验证》,不过就是偏向于前端的使用,今回我们看看怎么在 Spring Boot 下使用。
前期步骤参见旧文《免费使用 Google 防注册机验证》 即可。
配置类 GoolgeCaptachaConfig
配置 AppId 和密钥。
import com.ajaxjs.sdk_free.ClientAccessFullInfo;
/**
* 谷歌验证码配置
*
* @author Frank Cheung<sp42@qq.com>
*/
public class GoolgeCaptachaConfig extends ClientAccessFullInfo
private Boolean enable;
public Boolean isEnable()
return enable;
public void setEnable(Boolean enable)
this.enable = enable;
/**
* 客户端访问的基本两个字段: App Id、App 密钥
*
* @author Frank Cheung<sp42@qq.com>
*
*/
public abstract class ClientAccessFullInfo
/**
* App Id
*/
private String accessKeyId;
/**
* App 密钥
*/
private String accessSecret;
public String getAccessKeyId()
return accessKeyId;
public void setAccessKeyId(String accessKeyId)
this.accessKeyId = accessKeyId;
public String getAccessSecret()
return accessSecret;
public void setAccessSecret(String accessSecret)
this.accessSecret = accessSecret;
注入:
/**
* Captcha 配置
*
* @return
*/
@Bean
GoolgeCaptachaConfig goolgeCaptachaConfig()
GoolgeCaptachaConfig g = new GoolgeCaptachaConfig();
g.setEnable(true);
g.setAccessKeyId("6LclfLM----------------------");
g.setAccessSecret("6Lc-------------------------------");
return g;
如果你想在 yml 中配置,也是可以的,
# 谷歌验证码
GoolgeCaptacha:
accessKeyId: 6LclfLMZ-----------------
accessSecret: 6LclfL-------------------
注入的改为:
@Value("$GoolgeCaptacha.accessKeyId")
private String goolgeCaptachaAccessKeyId;
@Value("$GoolgeCaptacha.accessSecret")
private String goolgeCaptachaAccessSecret;
/**
* Captcha 配置
*
* @return
*/
@Bean
GoolgeCaptachaConfig goolgeCaptachaConfig()
GoolgeCaptachaConfig g = new GoolgeCaptachaConfig();
g.setEnable(true);
g.setAccessKeyId(goolgeCaptachaAccessKeyId);
g.setAccessSecret(goolgeCaptachaAccessSecret);
return g;
之所以还保留 GoolgeCaptachaConfig
,是因为 JSP 不知道怎么读取 yml,通过这个 bean 读取吧。
控制器的拦截器 GoogleCaptchaCheck/GoogleCaptchaMvcInterceptor
首先是前端页面,还是传统的 JSP 好用,
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"
import="com.ajaxjs.util.spring.DiContextUtil, com.ajaxjs.security.google_captcha.GoolgeCaptachaConfig"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Google reCAPTCHA</title>
<%
GoolgeCaptachaConfig g = DiContextUtil.getBean(GoolgeCaptachaConfig.class);
%>
<script
src="https://www.recaptcha.net/recaptcha/api.js?render=<%=g.getAccessKeyId()%>"></script>
</head>
<body>
<form>
<input type="text" name="foo" />
<button onclick="submitForm();return false;">提交</button>
</form>
<script type="text/javascript">
function submitForm()
grecaptcha.ready(() =>
grecaptcha.execute('<%=g.getAccessKeyId()%>', action: 'submit' ).then((token) =>
// Add your logic to submit to your backend server here.
let xhr = new XMLHttpRequest();
xhr.open("POST", '/cms/msg', true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); // 发送合适的请求头信息
xhr.onload = function()
console.log('请求完成')
;
let value = document.querySelector('input[name=foo]').value;
xhr.send('foo=' + value +'&grecaptchaToken='+ token);
);
);
</script>
</body>
</html>
在需要校验的 MVC 控制器某个方法中,加入这么一个注解 GoogleCaptchaCheck
就可以进行拦截。
控制器:
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.ajaxjs.framework.BaseController;
import com.ajaxjs.security.google_captcha.GoogleCaptchaCheck;
@Controller
@RequestMapping("/msg")
public class MsgController
@PostMapping(produces = MediaType.APPLICATION_JSON_VALUE + ";charset=utf-8")
@GoogleCaptchaCheck
@ResponseBody
public String create(@RequestParam String foo)
System.out.println(foo);
return BaseController.jsonOk("创建 Msg 成功");
注解源码:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 防止 Captcha
*
* @author Frank Cheung<sp42@qq.com>
*
*/
@Target( ElementType.METHOD, ElementType.TYPE )
@Retention(RetentionPolicy.RUNTIME)
public @interface GoogleCaptchaCheck
MVC 肯定得有个拦截器呀:
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
/**
*
* @author sp42 frank@ajaxjs.com
*
*/
public class GoogleCaptchaMvcInterceptor implements HandlerInterceptor
@Autowired
private GoogleFilter googleFilter;
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler)
if (handler instanceof HandlerMethod)
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
if (method != null)
String httpMethod = req.getMethod();
if (("POST".equals(httpMethod) || "PUT".equals(httpMethod)) && method.getAnnotation(GoogleCaptchaCheck.class) != null)
// 有注解,要检测
System.out.println("开始检测");
if (googleFilter.check(req))
return true;
return false;
return true;
怎么注册这个拦截器呢?在 Spring 的 WebMvcConfigurer
中注入 GoogleCaptchaMvcInterceptor
的 bean,然后再注册 addInterceptors(InterceptorRegistry registry)
。
/**
* 拦截器
*
* @return
*/
@Bean
GoogleCaptchaMvcInterceptor googleCaptchaMvcInterceptor()
return new GoogleCaptchaMvcInterceptor();
/**
* 加入拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry)
registry.addInterceptor(googleCaptchaMvcInterceptor());
super.addInterceptors(registry);
核心校验器 GoogleFilter
核心校验逻辑在 GoogleFilter
。
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import com.ajaxjs.framework.BaseController;
import com.ajaxjs.net.http.Post;
/**
* 校验核心
*
* @author sp42 frank@ajaxjs.com
*
*/
public class GoogleFilter
@Autowired
private GoolgeCaptachaConfig cfg;
/**
* 校验表单时候客户端传过来的 token 参数名
*/
public final static String PARAM_NAME = "grecaptchaToken";
/**
* 谷歌校验 API
*/
private final static String SITE_VERIFY = "https://www.recaptcha.net/recaptcha/api/siteverify";
/**
* 校验
*
* @return 是否通过验证,若为 true 表示通过,否则抛出异常
*/
public boolean check()
return check(BaseController.getRequest());
/**
* 校验
*
* @param request 请求对象
* @return 是否通过验证,若为 true 表示通过,否则抛出异常
*/
public boolean check(HttpServletRequest request)
return check(request.getParameter(PARAM_NAME));
/**
*
* @param token
* @return
*/
public boolean check(String token)
if (!cfg.isEnable())
return true;
if (!StringUtils.hasText(token))
throw new SecurityException("非法攻击!客户端缺少必要的参数");
Map<String, Object> map = Post.api(SITE_VERIFY, String.format("secret=%s&response=%s", cfg.getAccessSecret(), token.trim()));
if (map == null)
throw new IllegalAccessError("谷歌验证码服务失效,请联系技术人员");
if ((boolean) map.get("success")) // 判断用户输入的验证码是否通过
if (map.get("score") != null)
// 评分0 到 1。1:确认为人类,0:确认为机器人
double score = (double) map.get("score");
if (score < 0.5)
throw new SecurityException("验证码不通过,非法请求");
return true;
else
if ("timeout-or-duplicate".equals(map.get("error-codes")))
throw new NullPointerException("验证码已经过期,请刷新");
throw new SecurityException("验证码不正确");
校验通过,结果如下
其他问题
隐藏 reCAPTCHA 图标
使用 reCAPTCHA,会在网站上提示出一个图标.。
如果需要隐藏,可以添加 CSS。
.grecaptcha-badge
display: none;
依赖 js 过大
我的妈呀,300 多 k~
Vue SPA 下怎么使用
待续……
以上是关于Spring Boot 下使用谷歌 reCAPTCHA v3的主要内容,如果未能解决你的问题,请参考以下文章
带有 Google 的 OAuth2 - CORS 错误(Angular + Spring boot)[重复]
如何使用Spring Boot和嵌入式Tomcat配置此属性?