引言:
在学习springMVC 和Spring Security中,总有一个服务,叫做验证码,每次我自己写这个验证码的时候,最后还要在测试,而且特别的不友好,甚至我自己都分不清那个字母,当然Java里面有个很优秀的滑块验证码比如这个大佬开发的tianai-captcha
总是给人一种不是很优雅,我自己是人类,为什么还要进行验证码,而且这种验证往往经过训练的ai更容易比人类做的好,这就失去了验证码的意义CAPTCHA
,Cloud Flare就做了一个无需进行让你输入字符或者滑动以及选择图片的验证码,如果需要更多了解你可以访问CF的官方介绍CloudFlare-Turnstile
需要准备
- 一个Cloud Flare账号
- 一个域名(不需要接入Cloud Flare)
流程
CloudFlare以下简称CF, 官网的配置文件已经写的很清楚

- 用户打开受验证码保护的网页,先会和CF的验证码服务器进行通讯拿到,JWT令牌(令牌有效300秒)
- 用户会把提交内容以及CF生成的令牌发送到服务器,
- 服务器会拿着CF令牌进行和CF验证码服务器通讯,拿到JSON数值
快速使用
- 登录你的CF账号会在仪表盘的左侧看到
Turnstile
- 点击添加

我一般是选择托管,由cf来决定用户需不需要进行交互一下,
- 注册完毕之后我们会得到两个值一个是站点密钥,一个是密钥,密钥是用来进行服务端和CF通讯的,而站点密钥是放在被保护的资源上的。
按照CF文档的客户端配置我这里就那官方的例子来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script> <title>Document</title> </head>
<body> <form id="form" action="/login" method="POST"> <input id="username" type="text" placeholder="username" name="username" /> <input id="password" type="password" placeholder="password" name="password" /> <div class="cf-turnstile" data-sitekey="0x4AAAAAAACzpeJEUBQ4rBi-" data-callback="javascriptCallback"></div> </form> <button id="button" type="submit" value="Submit">Log in</button> </body> <script>
|
用户单击的登陆之后我们就能在后端拿到用户的账号密码以及CF的令牌。我们会在后端拿到一个名为:cf-turnstile-response
的键值对,这里面存放的是cf的令牌。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public String test(String req) throws IOException { String secret = ""; URL url = new URL("https://challenges.cloudflare.com/turnstile/v0/siteverify"); HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); httpConn.setRequestMethod("POST"); httpConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
httpConn.setDoOutput(true); OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream()); writer.write("secret="+secret+"&response="+req); writer.flush(); writer.close(); httpConn.getOutputStream().close();
InputStream responseStream = httpConn.getResponseCode() / 100 == 2 ? httpConn.getInputStream() : httpConn.getErrorStream(); Scanner s = new Scanner(responseStream).useDelimiter("\\A"); String response = s.hasNext() ? s.next() : ""; System.out.println(response); return response; }
|
上述是我们拿到用户的cf-turnstile-response
值然后和cf通讯返回的response
就是CF给我们的JSON。
根据官方文档:
如果验证成功,响应应类似于以下内容:
1 2 3 4 5 6 7 8
| { "success": true, "challenge_ts": "2022-02-28T15:14:30.096Z", "hostname": "example.com", "error-codes": [], "action": "login", "cdata": "sessionid-123456789" }
|
challenge_ts
是解决挑战时的 ISO 时间戳。
hostname
是为其提供质询的主机名。
action
是传递给客户端小部件的客户小部件标识符。这用于区分在分析中使用相同站点密钥的小部件。它的完整性受到攻击者修改的保护。建议验证操作是否与预期值匹配。
cdata
是传递给客户端小部件的客户数据。客户可以使用它来传达状态。它的完整性受到攻击者修改的保护。
error-codes
是发生的错误列表。
如果验证失败,响应应类似于以下内容:
1 2 3 4 5 6
| { "success": false, "error-codes": [ "invalid-input-response" ] }
|
success
通过将属性设置为 来指示验证错误false
。提供了错误代码列表以指示响应无法验证的原因。响应还可能包含其他字段,具体取决于 Turnstile siteverify 是否能够成功或不成功地解析响应。
错误代码:
错误代码 |
描述 |
missing-input-secret |
未传递秘密参数。 |
invalid-input-secret |
秘密参数无效或不存在。 |
missing-input-response |
未传递响应参数。 |
invalid-input-response |
响应参数无效或已过期。 |
bad-request |
该请求被拒绝,因为它格式不正确。 |
timeout-or-duplicate |
响应参数之前已经过验证。 |
internal-error |
验证响应时发生内部错误。可以重试该请求。 |
根据以上信息我们就可以判断并且,接收cf的保护了。
引用
CF Turnstile 开发文档
CloudFlare-Turnstile介绍