更新时间:2025-12-23 17:18:54
| 日期 | 版本 | 修订内容摘要 |
|---|---|---|
| 2023-09-07 | v1.0.0 | 更新接口文档 |
| 2025-05-08 | v2.0.0 | 启用行为验证码V2 JS地址 |
注意 App 客户端(Android/iOS/Harmony)当前仅支持通过 Webview 引入 H5页面进行接入。
复制成功import android.webkit.WebView; import android.webkit.WebSettings; import android.webkit.WebViewClient; import android.webkit.WebChromeClient;
复制成功<uses-permission android:name="android.permission.INTERNET"/> <application android:usesCleartextTraffic="true">...</application>
复制成功<WebView android:id="@+id/webview" android:layout_height="match_parent" android:layout_width="match_parent" />
复制成功import android.webkit.JavascriptInterface; public class JsBridge { @JavascriptInterface public void getData(String data) { System.out.println(data); } }
复制成功public class MainActivity extends AppCompatActivity { private WebView webview; private WebSettings webSettings; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { webview = (WebView) findViewById(R.id.webview); webSettings = webview.getSettings(); webSettings.setUseWideViewPort(true); webSettings.setLoadWithOverviewMode(true); // 禁用缓存 webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE); webview.setWebViewClient(new WebViewClient(){ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } }); // 开启js支持 webSettings.setJavaScriptEnabled(true); webview.addJavascriptInterface(new JsBridge(), "jsBridge"); // 也可以加载本地html(webView.loadUrl("file:///android_asset/xxx.html")) webview.loadUrl("https://x.x.x/x/"); } }
复制成功#import <WebKit/WebKit.h>
复制成功-(WKWebView *)webView{ if(_webView == nil){ //创建网页配置对象 WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; // 创建设置对象 WKPreferences *preference = [[WKPreferences alloc]init]; //设置是否支持 javaScript 默认是支持的 preference.javaScriptEnabled = YES; // 在 iOS 上默认为 NO,表示是否允许不经过用户交互由 javaScript 自动打开窗口 preference.javaScriptCanOpenWindowsAutomatically = YES; config.preferences = preference; //这个类主要用来做 native 与 JavaScript 的交互管理 WKUserContentController * wkUController = [[WKUserContentController alloc] init]; //注册一个name为jsToOcNoPrams的js方法 设置处理接收JS方法的对象 [wkUController addScriptMessageHandler:self name:@"jsToOcNoPrams"]; [wkUController addScriptMessageHandler:self name:@"jsToOcWithPrams"]; config.userContentController = wkUController; _webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) configuration:config]; // UI 代理 _webView.UIDelegate = self; // 导航代理 _webView.navigationDelegate = self; //此处即需要渲染的网页 NSString *path = [[NSBundle mainBundle] pathForResource:@"JStoOC.html" ofType:nil]; NSString *htmlString = [[NSString alloc]initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; [_webView loadHTMLString:htmlString baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]]; } return _webView; } [self.view addSubview:self.webView];
复制成功// 页面开始加载时调用 -(void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation { } // 页面加载失败时调用 -(void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { [self.progressView setProgress:0.0f animated:NO]; } // 当内容开始返回时调用 -(void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation { } // 页面加载完成之后调用 -(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { [self getCookie]; } //提交发生错误时调用 -(void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error { [self.progressView setProgress:0.0f animated:NO]; } // 接收到服务器跳转请求即服务重定向时之后调用 -(void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation { }
复制成功<p style="text-align:center"> <button id="btn2" type = "button" onclick = "jsToOcFunction()"> JS调用OC:带参数 </button> </p> function jsToOcFunction() { window.webkit.messageHandlers.jsToOcWithPrams.postMessage({"params":"res.randstr"}); }
复制成功-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ //此处message.body即传给客户端的json数据 //用message.body获得JS传出的参数体 NSDictionary * parameter = message.body; //JS调用OC if([message.name isEqualToString:@"jsToOcWithPrams"]){ //在此处客户端得到js透传数据 并对数据进行后续操作 parameter[@"params"] } }
复制成功// src/main/ets/view/Login.ets import router from '@ohos.router'; import web_webview from '@ohos.web.webview'; import { CaptchaCbParams,CaptchaRetData } from '../viewmodel/ParamsItem'; import Logger from '../common/utils/Logger'; import JSBridge from '../common/utils/JsBridge'; @Component export struct LoginComponent { controller: web_webview.WebviewController = new web_webview.WebviewController(); aboutToAppear() { // 配置web开启调试模式 web_webview.WebviewController.setWebDebuggingAccess(true); } ports: web_webview.WebMessagePort[] = []; // 初始化JS事件交互 private jsBridge: JSBridge = new JSBridge(this.controller, this); // 在build方法之外定义关闭WebView的方法 closeWebView(param: string) { Logger.info("接收的回调数据", param) const jsonObj: CaptchaCbParams = JSON.parse(param) const retObj: CaptchaRetData = JSON.parse(jsonObj.data) if (retObj.ret == 0) { // 验证成功有票据 router.pushUrl({ url: CommonConstants.SUCCESS_PAGE_URL, params: retObj }) } else { // 验证失败或者主动关闭验证码无票据 Logger.info("主动关闭验证码") } this.showWebView = false; } build() { Stack() { Web({ src: $rawfile('captcha.html'), // 本地HTML文件路径 controller: this.controller }) .domStorageAccess(true) .javaScriptAccess(true) .javaScriptProxy(this.jsBridge.javaScriptProxy) .onPageBegin(() => { this.jsBridge.initJsBridge(); }) .width('360vp') .height('360vp') .backgroundColor(Color.Transparent) .alignSelf(ItemAlign.Center) } } }
复制成功<!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"> <style> body { background-color: transparent; margin: 0; padding: 0; } </style> <title>Tencent Captcha</title> <script src="https://turing.captcha.qcloud.com/TCaptcha.js"></script> </head> <body> <script type="text/javascript"> function getAppIdParam() { let params = {}; let queryString = window.location.search.slice(1); // 获取URL参数字符串 if (queryString) { queryString = queryString.split('&'); // 分割成参数数组 queryString.forEach(item => { let arr = item.split('='); // 分割每个参数键值对 params[arr[0]] = decodeURIComponent(arr[1]); // decodeURIComponent用于解码URI }); } return params['appid'] || null; // 返回'appid'参数,如果不存在则返回null } function globalCallback(res) { // 与鸿蒙webview通信 if (window.ohosCallNative && window.ohosCallNative.callNative) { window.ohosCallNative.callNative('postMessage', JSON.stringify(res)); } } // captcha js error callback function errorCallback() { globalCallback({ ret: -1, randstr: '@' + Math.random().toString(36).substr(2), ticket: '', errorCode: 1001, errorMessage: 'jsload_error', }); } window.onload = function () { try { const sdkOptions = { isMobile: true, needFeedBack: false, enableDarkMode: false, // 是否需要loading和mask蒙层 loading: false, ready: function (size) { console.log('ready size:', JSON.stringify(size)); } }; const captcha = new TencentCaptcha('您的CatchaAppid', globalCallback, sdkOptions); // 调用方法,显示验证码 captcha.show(); } catch (error) { errorCallback(); } } </script> </body> </html>
复制成功// src/main/ets/common/utils/JsBridge.ets import WebView from '@ohos.web.webview'; import { code } from '../constants/CodeConstant'; import { ParamsItem } from '../../viewmodel/ParamsItem'; import { JavaScriptItem } from '../../viewmodel/JavaScriptItem'; import Logger from './Logger'; import { LoginComponent } from '../../view/LoginComponent'; /** * JS事件层 连接 WebView 和 ArkTS通信. */ export default class JsBridge { controller: WebView.WebviewController; private componentInstance: LoginComponent; constructor(controller: WebView.WebviewController,componentInstance: LoginComponent) { this.controller = controller; this.componentInstance = componentInstance; } get javaScriptProxy(): JavaScriptItem { let result: JavaScriptItem = { object: { call: this.call }, name: "JSBridgeHandle", methodList: ['call'], controller: this.controller } return result; } initJsBridge(): void { this.controller.runJavaScript(code); } postMessage = (params:string): Promise<string> => { // 可以在此将获取的票据传给页面,执行票据二次验证操作 Logger.info("验证码回调数据,",params) this.componentInstance.closeWebView(params); return new Promise((resolve) => { resolve(params); }) } call = (func: string, params: string): void => { const paramsObject: ParamsItem = JSON.parse(params); let result: Promise<string> = new Promise((resolve) => resolve('')); switch (func) { case 'postMessage': result = this.postMessage(params); break; default: break; } result.then((data: string) => { this.callback(paramsObject?.callID, data); }) } callback = (id: number, data: string): void => { this.controller.runJavaScript(`JSBridgeCallback("${id}", ${JSON.stringify(data)})`); } }
注意 业务客户端完成验证码接入后,服务端需二次核查验证码票据结果(未接入票据校验,会导致黑产轻易伪造验证结果,失去验证码人机对抗效果),详情请参见 [服务端接入> Web 及 App接入]。