Turnstile 需要浏览器环境,因为它是在访问者的浏览器中运行 JavaScript 质询的。在移动设备上,Turnstile 在移动端浏览器中工作而无需额外配置。
对于原生移动端应用程序,Turnstile 无法原生运行。相反,您需要使用 WebView(一种嵌入在原生应用程序内部的浏览器组件)来加载包含 Turnstile 小组件的网页。
WebView 在您的原生应用程序中嵌入了一个浏览器引擎,使您能够显示网页、表单和由 JavaScript 驱动的内容(如 Turnstile 小组件)。
为了让 Turnstile 在 WebView 中正常工作,必须满足以下要求。
- 必须启用 JavaScript 执行。
- DOM 存储 API 必须可用。
- 标准 Web API 必须可供访问。
- 对
challenges.cloudflare.com的访问。 - 支持 HTTP 和 HTTPS 连接。
- 允许连接到
about:blank和about:srcdoc。
- 整个会话中保持一致的 User Agent。
- 稳定的设备和浏览器特征。
- 不对核心浏览器行为进行修改。
WebView webView = findViewById(R.id.webview);
WebSettings webSettings = webView.getSettings();
// 必要项:启用 JavaScript
webSettings.setJavaScriptEnabled(true);
// 必要项:启用 DOM 存储
webSettings.setDomStorageEnabled(true);
// 推荐项:启用其他 Web 功能
webSettings.setLoadWithOverviewMode(true);
webSettings.setUseWideViewPort(true);
webSettings.setAllowFileAccess(true);
webSettings.setAllowContentAccess(true);
// 加载带有 Turnstile 的 Web 内容
webView.loadUrl("https://yoursite.com/protected-form");import WebKit
class ViewController: UIViewController {
@IBOutlet weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
// 配置 WebView
let configuration = WKWebViewConfiguration()
configuration.preferences.javaScriptEnabled = true
// 加载带有 Turnstile 的 Web 内容
if let url = URL(string: "https://yoursite.com/protected-form") {
webView.load(URLRequest(url: url))
}
}
}import { WebView } from "react-native-webview";
export default function App() {
return (
<WebView
source={{ uri: "https://yoursite.com/protected-form" }}
javaScriptEnabled={true}
domStorageEnabled={true}
allowsInlineMediaPlayback={true}
mediaPlaybackRequiresUserAction={false}
/>
);
}import 'package:flutter_inappwebview/flutter_inappwebview.dart';
class WebViewScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return InAppWebView(
initialUrlRequest: URLRequest(
url: Uri.parse('https://yoursite.com/protected-form')
),
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
javaScriptEnabled: true,
useShouldOverrideUrlLoading: false,
),
android: AndroidInAppWebViewOptions(
domStorageEnabled: true,
),
ios: IOSInAppWebViewOptions(
allowsInlineMediaPlayback: true,
),
),
);
}
}在会话期间更改 User Agent 会导致 Turnstile 质询失败,因为系统依赖一致的浏览器特征来验证访问者的真实性。当 User Agent 在会话中期发生改变时,Turnstile 会将其视为潜在的安全风险并拒绝质询。
// Android - 设置一致的 User Agent
webSettings.setUserAgentString(webSettings.getUserAgentString());// iOS - 保持默认的 User Agent
webView.customUserAgent = webView.value(forKey: "userAgent") as? String严格的内容安全策略 (Content Security Policy) 设置可能会阻止 Turnstile 加载必要的脚本并进行所需的网络连接。当 CSP 响应头或 meta 标签阻止访问 Turnstile 正常运行所需的域名和资源时,就会发生这种情况。
<meta
http-equiv="Content-Security-Policy"
content="
default-src 'self';
script-src 'self' challenges.cloudflare.com 'unsafe-inline';
connect-src 'self' challenges.cloudflare.com;
frame-src 'self' challenges.cloudflare.com;
"
/>WebView 安全限制可能会阻止访问 Turnstile 正常运行所需的域名。某些 WebView 被配置为仅允许特定的域名或阻止某些类型的连接,这可能会干扰 Turnstile 加载质询和与 Cloudflare 服务器进行通信的能力。
要解决此问题,请将您的 WebView 允许的源配置为包含 Turnstile 需要的所有域名:
challenges.cloudflare.comabout:blankabout:srcdoc- 您自己的域名
具体的配置方法因平台而异,但原则是显式允许这些域名的网络访问。
Cookie 和本地存储在会话之间未持久化可能会导致 Turnstile 失败,因为它依赖这些机制来维持状态并跟踪访问者行为。当 WebView 存储设置过于严格或应用程序在会话之间清除存储时,通常会发生这种情况。确保您的 WebView 配置为可以正确处理 cookie 和本地存储。
// Android - 启用 cookie
CookieManager.getInstance().setAcceptCookie(true);
CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);// iOS - 配置 cookie 存储
webView.configuration.websiteDataStore = WKWebsiteDataStore.default()