使用 Cloudflare Turnstile 实现 Java 后端的人机验证

该文章已生成可运行项目,

使用 Cloudflare Turnstile 实现 Java 后端的人机验证

时间:2025 年
标签:Java、Spring Boot、Cloudflare、验证码、人机验证


💡 背景

在现代 Web 应用中,表单接口经常成为恶意机器人的攻击目标。
传统的 Google reCAPTCHA 尽管流行,但存在隐私与加载延迟问题。
Cloudflare Turnstile 是 Cloudflare 推出的新一代无痕人机验证(CAPTCHA 替代方案),它轻量、隐私友好、加载快,并且完全免费

本文将带你一步步实现一个:
✅ 前端嵌入 Cloudflare Turnstile
✅ Java 后端验证 token 的完整流程。

最终效果如下:

用户打开表单 → 完成人机验证 → 后端验证通过 → 返回业务结果。


🧭 一、在 Cloudflare 控制台创建 Turnstile

  1. 登录 Cloudflare:
    👉 https://dash.cloudflare.com

  2. 左侧菜单中选择 Turnstile(或访问)
    https://dash.cloudflare.com/?to=/:account/turnstile

  3. 点击 “Add site”,填写:

    • Site name:任意,例如「My Java Demo」
    • Domain:可以选择 “Non-website”(用于本地测试)
    • Widget type:选择 “Managed”(推荐)
  4. 创建成功后,Cloudflare 会生成两段密钥:

    • Site key:用于前端页面加载组件
    • Secret key:用于后端验证 token

⚠️ 注意:
secret key 只能用于服务器端,切勿暴露在前端或版本库中。


🧩 二、前端页面(HTML)

前端只需要引入 Cloudflare 的脚本并嵌入验证组件。

文件:src/main/resources/static/index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <title>Cloudflare Turnstile + Java Demo</title>
  <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
</head>
<body style="font-family:sans-serif;margin:50px;">
  <h2>Cloudflare Turnstile + Java 后端验证</h2>

  <form action="/verify" method="post">
    <label>姓名:</label>
    <input type="text" name="name" required /><br><br>

    <!-- Turnstile 验证组件 -->
    <div class="cf-turnstile"
         data-sitekey="替换为你的_SITE_KEY"
         data-theme="light">
    </div>

    <br>
    <button type="submit">提交</button>
  </form>
</body>
</html>

当用户完成验证后,Turnstile 会自动在表单中生成一个隐藏字段:
cf-turnstile-response
它就是我们需要传给后端验证的 token。


💻 三、后端实现(Spring Boot)

1. 依赖配置(pom.xml

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
  </dependency>
</dependencies>

2. 控制器逻辑(TurnstileController.java

package com.example.demo;

import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Controller;

import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;

@Controller
public class TurnstileController {
    private static final String VERIFY_URL = "https://challenges.cloudflare.com/turnstile/v0/siteverify";
    private final HttpClient httpClient = HttpClient.newHttpClient();
    private final ObjectMapper json = new ObjectMapper();

    @PostMapping("/verify")
    @ResponseBody
    public String verifyForm(@RequestParam String name,
                             @RequestParam("cf-turnstile-response") String token,
                             @RequestHeader(value = "X-Forwarded-For", required = false) String ip) throws Exception {

        // 从环境变量读取 secret(推荐方式)
        String secret = System.getenv("TURNSTILE_SECRET");
        if (secret == null || secret.isEmpty()) {
            return "❌ 未配置环境变量 TURNSTILE_SECRET";
        }

        // 构造表单参数
        String body = "secret=" + URLEncoder.encode(secret, StandardCharsets.UTF_8)
                + "&response=" + URLEncoder.encode(token, StandardCharsets.UTF_8);
        if (ip != null) {
            body += "&remoteip=" + URLEncoder.encode(ip, StandardCharsets.UTF_8);
        }

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(VERIFY_URL))
                .timeout(Duration.ofSeconds(10))
                .header("Content-Type", "application/x-www-form-urlencoded")
                .POST(HttpRequest.BodyPublishers.ofString(body))
                .build();

        HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

        Map<String, Object> result = json.readValue(response.body(), Map.class);
        boolean success = Boolean.TRUE.equals(result.get("success"));

        if (success) {
            return "✅ 验证成功!你好," + name + "。";
        } else {
            return "❌ 验证失败:" + result;
        }
    }
}

⚙️ 四、运行与测试

  1. 设置环境变量(替换为你的 secret key):

    export TURNSTILE_SECRET="你的_secret_key"
    

    Windows:

    set TURNSTILE_SECRET=你的_secret_key
    
  2. 启动项目:

    mvn spring-boot:run
    
  3. 访问:

    http://localhost:8080/
    
  4. 填写表单、完成人机验证,点击提交。
    如果验证成功,你将看到类似:

    ✅ 验证成功!你好,张三。
    

🔍 五、验证逻辑解析

后端调用的接口是:

POST https://challenges.cloudflare.com/turnstile/v0/siteverify

请求参数:

参数名说明
secret你的服务器端密钥
response前端表单中的 cf-turnstile-response
remoteip(可选)用户的真实 IP

返回示例:

{
  "success": true,
  "challenge_ts": "2025-10-16T10:01:32Z",
  "hostname": "localhost",
  "action": "login"
}

🔒 六、安全与最佳实践

建议项说明
Secret 管理放环境变量或密钥管理系统,绝对不要写入代码或前端
Token 有效期Turnstile token 有效期 5 分钟,仅能使用一次
IP 校验使用 CF-Connecting-IPX-Forwarded-For 获取真实 IP
错误码timeout-or-duplicateinvalid-input-secret 等进行日志记录
HTTPS确保生产环境走 HTTPS,防止中间人攻击

🌈 七、总结

Cloudflare Turnstile 的最大优点是:

  • 免费、轻量、隐私友好;
  • 无需用户手动点选验证码;
  • 接入简单,只需前端 + 一次 HTTP 验证。

相比 Google reCAPTCHA:

  • 不依赖第三方追踪;
  • 不要求 Google 账号;
  • 验证更流畅、延迟更低。

对于希望提高表单安全性、又不想牺牲用户体验的 Java 开发者来说,Turnstile 是一个非常理想的选择。


🧰 参考文档

本文章已经生成可运行项目
### Cloudflare Turnstile 使用指南和集成方法 #### 什么是Cloudflare TurnstileCloudflare Turnstile 是一种现代的验证码解决方案,旨在保护网站免受自动化攻击的同时提供更好的用户体验。与传统的 reCAPTCHA 不同,Turnstile 提供了更简洁的设计以及更高的隐私保障[^1]。 #### 如何注册并获取站点密钥? 为了使用 Turnstile 验证码服务,开发者需要先访问 [Cloudflare 官方网站](https://www.cloudflare.com/turnstile/) 注册账号,并创建一个新的站点来获得一对公开密钥(sitekey)和私有密钥(secret key)。这两个密钥将在后续配置过程中被广泛用于前端显示验证组件及后端校验请求合法性[^2]。 #### 前端页面集成 要在网页上部署 Turnstile 小部件,需按照如下方式引入 JavaScript 文件: ```html <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script> ``` 接着,在 HTML 表单内添加相应的 div 标签以承载该控件实例化的位置: ```html <div class="form-group"> <div id="cf-turnstile"></div> </div> <!-- 初始化 Turnstile --> <script type="text/javascript"> window.onload = function() { var turnstileCallback = function(token) { console.log('Verification successful:', token); }; grecaptcha.ready(function() { grecaptcha.execute('<your-site-key>', {action:'submit'}).then(turnstileCallback); }); }; </script> ``` 请注意替换 `<your-site-key>` 占位符为你自己的 sitekey[^3]。 #### 后端服务器验证 当用户提交表单数据时,会一并将通过隐藏字段传递过来的令牌发送给服务器侧进行进一步确认。此时可以通过 HTTP POST 请求向 `https://challenges.cloudflare.com/turnstile/siteverify` 发送包含 secret_key 和 response 参数的消息体来进行真实性检验。成功返回 JSON 对象中 score 字段表示此次交互行为可信度得分;若低于设定阈值则视为可疑操作处理[^4]。 ```python import requests def verify_turnstile_token(secret, response): url = "https://challenges.cloudflare.com/turnstile/siteverify" payload = {'secret': secret, 'response': response} r = requests.post(url, data=payload) result = r.json() return result['success'], result.get('score') ```
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

上善若水-学者至上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值