flutter 人机验证实战

先看效果

基本思路

接口进行触发是否进行图像验证,验证后将结果携带到接口里面去,进行人机验证

使用的技术(可惜只有web版本的)

验证码2.0智能人机验证(VAPTCHA)- 安全、易用、完全免费手势验证码VAPTCHA是基于人工智能和大数据的次世代人机验证解决方案。独有的验证策略及风控模型组合可彻底杜绝刷票、灌水、撞库等恶意攻击行为。https://www.vaptcha.com/

本来想使用flutter 实现一个插件 来调用安卓的ios,写的过程发现 vaptcha 的原生使用的是webview实现的,额 还是使用web版本进行封装把

代码如下

webview代码

Dart 复制代码
import 'dart:convert';
import 'dart:io';

import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class AppWebView extends StatefulWidget {
  final String url;
  final Function(dynamic)? onMessageReceived;
  const AppWebView({
    super.key,
    required this.url,
    this.onMessageReceived,
  });

  @override
  State<AppWebView> createState() => _AppWebViewState();
}

class _AppWebViewState extends State<AppWebView> {
  late final WebViewController controller;

  int progress = 0;

  @override
  void initState() {
    super.initState();
    controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..addJavaScriptChannel("lh", onMessageReceived: onMessageReceived)
      ..enableZoom(true)
      ..setBackgroundColor(const Color(0x00000000))
      ..setNavigationDelegate(
        NavigationDelegate(
          onProgress: (int progress) {
            // Update loading bar.
            this.progress = progress;
            setState(() {});
          },
          onPageStarted: (String url) {},
          onPageFinished: (String url) {},
          onWebResourceError: (WebResourceError error) {},
          onNavigationRequest: (NavigationRequest request) {
            if (request.url.startsWith('https://www.youtube.com/')) {
              return NavigationDecision.prevent;
            }
            return NavigationDecision.navigate;
          },
        ),
      )
      ..loadRequest(Uri.parse(widget.url));
  }

  // 接受h5发送来的数据
  onMessageReceived(message) async {
    widget.onMessageReceived?.call(message);
    //接收H5发过来的数据
    String sendMesStr = message.message;
    print("onMessageReceived sendMesStr:${sendMesStr}");
    Map<String, dynamic> msg = json.decode(sendMesStr);

    String method = msg["method"] ?? "";
    // Map<String, dynamic> data = msg["data"] ?? {};
    if (method.isNotEmpty) {
      switch (method) {
        case "back":
          controller.goBack();
          break;
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    if (!kIsWeb && (Platform.isIOS || Platform.isAndroid)) {
      return Scaffold(
        // appBar: AppBar(title: const Text('Flutter Simple Example')),
        body: Stack(children: [
          WebViewWidget(controller: controller),
          if (progress != 100)
            const Center(
              child: CupertinoActivityIndicator(),
            )
        ]),
      );
    } else {
      return const Center(
        child: Text('WebView control is not supported on this platform yet.'),
      );
    }
  }
}

封装的核心

Dart 复制代码
import 'dart:convert';

import 'package:LS/common/api/api.dart';
import 'package:LS/common/utils/toast.dart';
import 'package:LS/common/widgets/app_webview.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';

class Captcha {
  static show() {
    AppToast.dialog(
      title: "图像验证".tr,
      showBtn: false,
      SizedBox(
        height: 350.h,
        child: const AppWebView(
          url: "https://genaertest.wbnr.xyz/captcha",
          onMessageReceived: onMessageReceived,
        ),
      ),
    );
  }

  static onMessageReceived(message) async {
    //接收H5发过来的数据
    String sendMesStr = message.message;
    print("onMessageReceived sendMesStr:${sendMesStr}");
    Map<String, dynamic> msg = json.decode(sendMesStr);

    String method = msg["method"] ?? "";
    Map<String, dynamic> data = msg["data"] ?? {};
    String server = data["server"] ?? "";
    String token = data["token"] ?? "";
    if (method.isNotEmpty) {
      switch (method) {
        case "getServerToken":
          try {
            // 调用后端接口
            final res = await Api.captchaVerify(server: server, token: token);
            final data = res["data"] ?? {};
            if (data["success"] == 1) {
              AppToast.show("验证通过");
            }
            // 关闭 SmartDialog  弹窗
            SmartDialog.dismiss();
          } catch (e) {
            SmartDialog.dismiss();
          }
          break;
      }
    }
  }
}

弹窗使用的是flutter_smart_dialog 可以自己集成一下

flutter_smart_dialog | Flutter PackageAn elegant Flutter Dialog solution, Easily implement Toast, Loading and custom Dialog, Make the use of the dialog easier!https://pub-web.flutter-io.cn/packages/flutter_smart_dialog

AppToast.dialog 其实是下面的封装

SmartDialog.show(

builder: (_) {

return child;

},

keepSingle: true,

clickMaskDismiss: clickMaskDismiss,

usePenetrate: false,

maskWidget: maskWidget,

);

然后是网页代码

Dart 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title></title>
    <style>
      /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
      html {
        line-height: 1.15;
        -webkit-text-size-adjust: 100%;
      }
      body {
        margin: 0;
      }
      main {
        display: block;
      }
      h1 {
        font-size: 2em;
        margin: 0.67em 0;
      }
      hr {
        box-sizing: content-box;
        height: 0;
        overflow: visible;
      }
      pre {
        font-family: monospace, monospace;
        font-size: 1em;
      }
      a {
        background-color: transparent;
      }
      abbr[title] {
        border-bottom: none;
        text-decoration: underline;
        text-decoration: underline dotted;
      }
      b,
      strong {
        font-weight: bolder;
      }
      code,
      kbd,
      samp {
        font-family: monospace, monospace;
        font-size: 1em;
      }
      small {
        font-size: 80%;
      }
      sub,
      sup {
        font-size: 75%;
        line-height: 0;
        position: relative;
        vertical-align: baseline;
      }
      sub {
        bottom: -0.25em;
      }
      sup {
        top: -0.5em;
      }
      img {
        border-style: none;
      }
      button,
      input,
      optgroup,
      select,
      textarea {
        font-family: inherit;
        font-size: 100%;
        line-height: 1.15;
        margin: 0;
      }
      button,
      input {
        overflow: visible;
      }
      button,
      select {
        text-transform: none;
      }
      [type="button"],
      [type="reset"],
      [type="submit"],
      button {
        -webkit-appearance: button;
      }
      [type="button"]::-moz-focus-inner,
      [type="reset"]::-moz-focus-inner,
      [type="submit"]::-moz-focus-inner,
      button::-moz-focus-inner {
        border-style: none;
        padding: 0;
      }
      [type="button"]:-moz-focusring,
      [type="reset"]:-moz-focusring,
      [type="submit"]:-moz-focusring,
      button:-moz-focusring {
        outline: 1px dotted ButtonText;
      }
      fieldset {
        padding: 0.35em 0.75em 0.625em;
      }
      legend {
        box-sizing: border-box;
        color: inherit;
        display: table;
        max-width: 100%;
        padding: 0;
        white-space: normal;
      }
      progress {
        vertical-align: baseline;
      }
      textarea {
        overflow: auto;
      }
      [type="checkbox"],
      [type="radio"] {
        box-sizing: border-box;
        padding: 0;
      }
      [type="number"]::-webkit-inner-spin-button,
      [type="number"]::-webkit-outer-spin-button {
        height: auto;
      }
      [type="search"] {
        -webkit-appearance: textfield;
        outline-offset: -2px;
      }
      [type="search"]::-webkit-search-decoration {
        -webkit-appearance: none;
      }
      ::-webkit-file-upload-button {
        -webkit-appearance: button;
        font: inherit;
      }
      details {
        display: block;
      }
      summary {
        display: list-item;
      }
      template {
        display: none;
      }
      [hidden] {
        display: none;
      }
      /*# sourceMappingURL=normalize.min.css.map */
    </style>
    <style>
      .VAPTCHA-init-main {
        display: table;
        width: 100%;
        height: 100%;
        background-color: #eeeeee;
        position: fixed;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
      }

      .VAPTCHA-init-loading {
        display: table-cell;
        vertical-align: middle;
        text-align: center;
      }

      .VAPTCHA-init-loading > a {
        display: inline-block;
        width: 18px;
        height: 18px;
        border: none;
      }

      .VAPTCHA-init-loading .VAPTCHA-text {
        font-family: sans-serif;
        font-size: 12px;
        color: #cccccc;
        vertical-align: middle;
      }
      #VAPTCHAContainer {
        width: 240px;
        height: 36px;
        position: fixed;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
      }
    </style>
    <!-- <script src="https://v-cn.vaptcha.com/v3.js"></script> -->
    <script>
      !(function () {
        "use strict";
        function e(e) {
          return e === undefined || null === e;
        }
        function t(e) {
          return e !== undefined && null !== e;
        }
        function n(e) {
          return null !== e && "object" === (void 0 === e ? "undefined" : p(e));
        }
        function a(e) {
          return (
            "object" === (void 0 === e ? "undefined" : p(e)) &&
            e instanceof HTMLElement
          );
        }
        function r(e) {
          var t = e && e.toString().match(/^\s*function (\w+)/);
          return t ? t[1] : "";
        }
        function o(e) {
          var t =
            arguments.length > 1 && arguments[1] !== undefined
              ? arguments[1]
              : {};
          for (var n in t) e[n] = t[n];
          return e;
        }
        function i(e) {
          var t = Object.create(null);
          return function (n) {
            return t[n] || (t[n] = e(n));
          };
        }
        function c(e) {
          return h.call(e).slice(8, -1);
        }
        function u(e) {
          throw new Error(e);
        }
        function l() {
          if (navigator.cookieEnabled) {
            localStorage.setItem("vaptchatemp1", "1");
            var e = localStorage.getItem("vaptchatemp1");
            return localStorage.removeItem("vaptchatemp1"), !!e;
          }
          return !1;
        }
        function Promise(e) {
          var t = this;
          (this.state = "pending"),
            (this.value = undefined),
            (this.reason = undefined),
            (this.onResolveAsyncCallbacks = []),
            (this.onRejectAsyncCallbacks = []);
          var n = function (e) {
              "pending" === t.state &&
                ((t.state = "fulfilled"),
                (t.value = e),
                t.onResolveAsyncCallbacks.map(function (e) {
                  return e();
                }));
            },
            a = function (e) {
              "pending" === t.state &&
                ((t.state = "rejected"),
                (t.reason = e),
                t.onRejectAsyncCallbacks.map(function (t) {
                  return t(e);
                }));
            };
          try {
            e(n, a);
          } catch (r) {
            a(r);
          }
        }
        function s(e, t) {
          if (!(e instanceof t))
            throw new TypeError("Cannot call a class as a function");
        }
        function f() {
          var e = navigator.language || navigator.userLanguage;
          return "zh-CN" === e
            ? "zh-CN"
            : "zh-TW" === e
            ? "zh-TW"
            : e.includes("en", -1)
            ? "en"
            : e.includes("ja", -1)
            ? "jp"
            : e.includes("vi", -1)
            ? "vi"
            : "zh-CN";
        }
        (window.HTMLElement = window.HTMLElement || Element),
          Array.prototype.map ||
            (Array.prototype.map = function (e, t) {
              var n, a, r;
              if (null == this)
                throw new TypeError(" this is null or not defined");
              var o = Object(this),
                i = o.length >>> 0;
              if ("[object Function]" != Object.prototype.toString.call(e))
                throw new TypeError(e + " is not a function");
              for (t && (n = t), a = new Array(i), r = 0; r < i; ) {
                var c, u;
                r in o && ((c = o[r]), (u = e.call(n, c, r, o)), (a[r] = u)),
                  r++;
              }
              return a;
            }),
          Array.prototype.includes ||
            (Array.prototype.includes = function (e, t) {
              if (null == this)
                throw new TypeError('"this" is null or not defined');
              var n = Object(this),
                a = n.length >>> 0;
              if (0 === a) return !1;
              for (
                var r = 0 | t, o = Math.max(r >= 0 ? r : a - Math.abs(r), 0);
                o < a;

              ) {
                if (n[o] === e) return !0;
                o++;
              }
              return !1;
            }),
          Array.prototype.findIndex ||
            (Array.prototype.findIndex = function (e) {
              if (null == this)
                throw new TypeError('"this" is null or not defined');
              var t = Object(this),
                n = t.length >>> 0;
              if ("function" != typeof e)
                throw new TypeError("predicate must be a function");
              for (var a = arguments[1], r = 0; r < n; ) {
                if (e.call(a, t[r], r, t)) return r;
                r++;
              }
              return -1;
            }),
          Object.create ||
            (Object.create = function (e) {
              var t = function () {};
              return (t.prototype = e), new t();
            });
        var d = {
            vid: null,
            scene: 0,
            container: null,
            mode: "popup",
            style: "dark",
            lang: "auto",
            ai: !0,
            https: !0,
            guide: !0,
            aiAnimation: !1,
            protocol: "https://",
            css_version: "2.9.12",
            cdn_servers: ["statics.vaptcha.com"],
            api_server: "api.vaptcha.com/v3",
            canvas_path: "/canvas.min.js",
            offline_server: "",
          },
          p =
            "function" == typeof Symbol && "symbol" == typeof Symbol.iterator
              ? function (e) {
                  return typeof e;
                }
              : function (e) {
                  return e &&
                    "function" == typeof Symbol &&
                    e.constructor === Symbol &&
                    e !== Symbol.prototype
                    ? "symbol"
                    : typeof e;
                },
          h = Object.prototype.toString,
          v =
            (i(function (e) {
              for (
                var t = {},
                  n =
                    (e && -1 !== e.indexOf("?") && e.split("?")[1]) ||
                    window.location.search.substring(1),
                  a = n.split("&"),
                  r = 0;
                r < a.length;
                r++
              ) {
                var o = a[r].split("=");
                t[decodeURIComponent(o[0])] = decodeURIComponent(o[1]);
              }
              return t;
            }),
            i(function (e) {
              return e.charAt(0).toUpperCase() + e.slice(1);
            })),
          g = function (t) {
            (this.data = t),
              (this.valiudateFuns = []),
              (this.ruleFuns = {
                required: function (t, n) {
                  return e(t) || 0 === t.length ? n : null;
                },
              });
          };
        g.prototype = {
          constructor: g,
          addValidateRules: function (e) {
            o(this.ruleFuns, e);
          },
          add: function (e, t, n) {
            var a = this,
              r = t.split(":"),
              o = r.shift(),
              i = this.ruleFuns[o];
            r.unshift(this.data[e]),
              r.push(n),
              i
                ? this.valiudateFuns.push(function () {
                    return i.apply(a, r);
                  })
                : console.warn(
                    "Validator warning: rule " + o + " is not defined"
                  );
          },
          validate: function () {
            for (var e, t = 0; (e = this.valiudateFuns[t++]); ) {
              var n = e();
              if (n) return u(n), !1;
            }
            return !0;
          },
        };
        var m = {
          AccessDenied: "0101",
          RefreshAgain: "0102",
          Success: "0103",
          Fail: "0104",
          RefreshTooFast: "0105",
          RefreshTanto: "0106",
          DrawTanto: "0107",
          Attack: "0108",
          jsonpTimeOut: "0703",
          challengeExpire: "1002",
        };
        (Promise.prototype.then = function (e) {
          var t = this;
          if ("fulfilled" === this.state) {
            var a = e(this.value);
            if (n(a) && "Promise" === r(a.constructor)) return a;
          }
          return "pending" === this.state
            ? new Promise(function (a) {
                t.onResolveAsyncCallbacks.push(function () {
                  var o = e(t.value);
                  if (n(o) && "Promise" === r(o.constructor)) return o.then(a);
                  a(o);
                });
              })
            : this;
        }),
          (Promise.prototype["catch"] = function (e) {
            return (
              "rejected" === this.state && e(this.reason),
              "pending" === this.state && this.onRejectAsyncCallbacks.push(e),
              this
            );
          }),
          (Promise.resolve = function (e) {
            return new Promise(function (t) {
              t(e);
            });
          }),
          (Promise.reject = function (e) {
            return new Promise(function (t, n) {
              n(e);
            });
          });
        var y = (function () {
            function e(e, t) {
              for (var n = 0; n < t.length; n++) {
                var a = t[n];
                (a.enumerable = a.enumerable || !1),
                  (a.configurable = !0),
                  "value" in a && (a.writable = !0),
                  Object.defineProperty(e, a.key, a);
              }
            }
            return function (t, n, a) {
              return n && e(t.prototype, n), a && e(t, a), t;
            };
          })(),
          w = (function () {
            function e() {
              s(this, e);
            }
            return (
              y(e, [
                {
                  key: "GenerateFP",
                  value: function () {
                    return this.extractCRC32FromBase64(
                      this.getComplexCanvasFingerprint(
                        arguments.length > 0 && arguments[0] !== undefined
                          ? arguments[0]
                          : ""
                      )
                    );
                  },
                },
                {
                  key: "getComplexCanvasFingerprint",
                  value: function () {
                    var e =
                        arguments.length > 0 && arguments[0] !== undefined
                          ? arguments[0]
                          : "",
                      t = "BrowserLeaks,com <canvas> 1.0" + e,
                      n = document.createElement("canvas");
                    n.setAttribute("width", "220"),
                      n.setAttribute("height", "30");
                    var a = n.getContext("2d");
                    return (
                      (a.textBaseline = "top"),
                      (a.font = "14px 'Arial'"),
                      (a.textBaseline = "alphabetic"),
                      (a.fillStyle = "#f60"),
                      a.fillRect(125, 1, 62, 20),
                      (a.fillStyle = "#069"),
                      a.fillText(t, 2, 15),
                      (a.fillStyle = "rgba(102, 204, 0, 0.7)"),
                      a.fillText(t, 4, 17),
                      n.toDataURL()
                    );
                  },
                },
                {
                  key: "extractCRC32FromBase64",
                  value: function (e) {
                    return (
                      (e = e.replace("data:image/png;base64,", "")),
                      this.string2Hex(atob(e).slice(-16, -12).toString())
                    );
                  },
                },
                {
                  key: "string2Hex",
                  value: function (e) {
                    for (var t = "", n = 0; n < e.length; n++) {
                      var a = e.charCodeAt(n);
                      a <= 15 && (t += "0"),
                        (t += a.toString(16).toLocaleUpperCase());
                    }
                    return t;
                  },
                },
              ]),
              e
            );
          })(),
          T =
            (new w(),
            (function () {
              var e = d.protocol,
                t = d.api_server,
                n = function (e) {
                  var t = "";
                  for (var n in e)
                    Object.prototype.hasOwnProperty.call(e, n) &&
                      (t += "&" + n + "=" + encodeURIComponent(e[n]));
                  return t;
                },
                a = function (a, r) {
                  var o = n(r),
                    i = a.indexOf("http://") > -1 || a.indexOf("https://") > -1;
                  return (
                    a.indexOf("?") < 0 && (o = "?" + o.slice(1)),
                    i ? "" + a + o : "" + e + t + a + o
                  );
                },
                r = function (e) {
                  var t = document.getElementsByTagName("head")[0],
                    n = document.createElement("script");
                  return (
                    (n.charset = "UTF-8"),
                    (n.src = e),
                    t.appendChild(n),
                    {
                      remove: function () {
                        t.removeChild(n);
                      },
                    }
                  );
                },
                i = function (e, t, n, i) {
                  return (
                    (t = t || {}),
                    (n = n || !1),
                    new Promise(function (c) {
                      if (n) {
                        var u = setTimeout(function () {
                          clearTimeout(u),
                            l.remove(),
                            c({ code: "0703", msg: "Time out,Refresh Again!" });
                        }, i || 2e3);
                        window["static"] = function () {
                          clearTimeout(u), c.apply(this, arguments), l.remove();
                        };
                        var l = r(e);
                      } else {
                        var s = "VaptchaJsonp" + new Date().valueOf();
                        window[s] && (s += "1"),
                          o(t, { callback: s }),
                          (e = a(e, t));
                        var f = r(e),
                          d = setTimeout(function () {
                            clearTimeout(d),
                              (window[s] = null),
                              f.remove(),
                              c({
                                code: "0703",
                                msg: "Time out,Refresh Again!",
                              });
                          }, 1e4);
                        window[s] = function () {
                          clearTimeout(d),
                            c.apply(this, arguments),
                            f.remove(),
                            (window[s] = null);
                        };
                      }
                    })
                  );
                };
              return (
                (i.setConfig = function (n) {
                  (e = n.protocol || e), (t = n.api_server || t);
                }),
                i
              );
            })()),
          S = {
            staticConfig: function (e) {
              return T(
                e.protocol + e.url + e.type + e.id,
                {},
                !0,
                e.waitTime || 2e3
              );
            },
            getConfig: function (e) {
              var n = "";
              return (
                l() &&
                  t(localStorage.getItem("vaptchanu")) &&
                  (n = localStorage.getItem("vaptchanu")),
                T("/config", {
                  vi: e.vid,
                  t: e.mode,
                  s: e.scene || 0,
                  z: e.zone,
                  v: 3,
                  u: n,
                })
              );
            },
            lang: function (e) {
              return T("http://localhost:8080/api/v1/lang", {}, !1);
            },
          },
          C = {
            en: {
              "0201": "id empty",
              "0202": "id error",
              "0208": "scene error",
              "0209": "request used up",
              "0906": "params error",
              "0702": "VAPTCHA unit does not match the domain name",
              "0105": "Request too fast, try again later!",
            },
            "zh-CN": {
              "0702": "验证单元与域名不匹配",
              "0105": "刷新过快,请稍后再试。",
            },
          },
          b = 0,
          j = 9999999999999,
          I = 0,
          N = 9999999999999,
          A = (function () {
            function r() {
              var e = navigator.language || navigator.userLanguage;
              return "zh-CN" === e
                ? "zh-CN"
                : "zh-TW" === e
                ? "zh-TW"
                : e.includes("en", -1)
                ? "en"
                : e.includes("ja", -1)
                ? "jp"
                : "zh-CN";
            }
            function i(e) {
              if ("cn" === e.area) return "cn";
              if ("sea" === e.area) return "sea";
              if ("na" === e.area) return (e.area = "sea"), "sea";
              if (
                t(localStorage.getItem("vaptchaNetway")) &&
                "" !== localStorage.getItem("vaptchaNetway")
              )
                return (
                  o(e, { area: localStorage.getItem("vaptchaNetway") }),
                  localStorage.getItem("vaptchaNetway")
                );
              var n = 0 - new Date().getTimezoneOffset() / 60,
                a = navigator.language || window.navigator.userLanguage;
              return (
                (a = a.toLowerCase()),
                8 === n && "zh-cn" === a
                  ? (o(e, { area: "cn" }), "cn")
                  : 8 === n && "zh-cn" !== a
                  ? (o(e, { area: "sea" }), "sea")
                  : (n >= 6 && n < 8) || (n > 8 && n <= 10)
                  ? (o(e, { area: "sea" }), "na")
                  : (o(e, { area: "na" }), "na")
              );
            }
            var l = !1,
              s = function (e) {
                var n = void 0,
                  a = void 0;
                return (
                  t(e.area)
                    ? ((n = "channel-" + i(e) + ".vaptcha.net/" + i(e)),
                      (a = "api-" + i(e) + ".vaptcha.net"))
                    : (o(e, { area: "cn" }),
                      (n = "channel-cn.vaptcha.net/cn"),
                      (a = "api-cn.vaptcha.net")),
                  (b = new Date().getTime()),
                  t(localStorage.getItem("vaptchaSpareCh")) &&
                  new Date().getTime() -
                    localStorage.getItem("vaptchaSpareCh") <
                    36e5
                    ? S.staticConfig({
                        protocol: e.protocol,
                        id: e.vid,
                        url: n,
                        type: "/config/",
                      }).then(function (a) {
                        if (
                          ((j = new Date().getTime()), w(i(e), e), t(a.alias))
                        )
                          return S.staticConfig({
                            protocol: e.protocol,
                            id: a.alias,
                            url: n,
                            type: "/alias/",
                          }).then(function (e) {
                            return o(e, { state: a.state }), Promise.resolve(e);
                          });
                        console.log("channel error");
                      })
                    : S.staticConfig({
                        protocol: e.protocol,
                        url: a,
                        type: "/channel/",
                        id: e.vid,
                        waitTime: 5e3,
                      }).then(function (a) {
                        return (
                          (j = new Date().getTime()),
                          w(i(e), e),
                          t(a.msg)
                            ? (localStorage.setItem(
                                "vaptchaSpareCh",
                                new Date().getTime()
                              ),
                              S.staticConfig({
                                protocol: e.protocol,
                                id: e.vid,
                                url: n,
                                type: "/config/",
                              }).then(function (a) {
                                if (t(a.alias))
                                  return S.staticConfig({
                                    protocol: e.protocol,
                                    id: a.alias,
                                    url: n,
                                    type: "/alias/",
                                  }).then(function (e) {
                                    return (
                                      o(e, { state: a.state }),
                                      Promise.resolve(e)
                                    );
                                  });
                                console.log("channel error");
                              }))
                            : Promise.resolve(a)
                        );
                      })
                );
              },
              f = function (e) {
                return s(e)
                  .then(function (n) {
                    if (t(n.code) && "0703" === n.code)
                      return (
                        u("5001: channel interface timeout"),
                        Promise.reject("5001: channel interface timeout")
                      );
                    o(e, { api_server: n.api }), T.setConfig(e);
                    var a = 0 - new Date().getTimezoneOffset() / 60;
                    return o(e, { zone: a }), S.getConfig(e);
                  })
                  .then(function (n) {
                    if (t(n.code) && "0703" === n.code)
                      return (
                        u("5002: config interface timeout"),
                        Promise.reject("5002: config interface timeout")
                      );
                    if (!n) return Promise.resolve();
                    if (n.code !== m.Success) {
                      var a = C.en;
                      return (
                        "0702" === n.msg
                          ? alert("" + a[n.msg])
                          : "0105" === n.code
                          ? alert("" + a[n.code])
                          : console.log("errorCode:" + n.code + ":" + n.msg),
                        u(a[n.msg] || n.msg),
                        Promise.reject(n.code)
                      );
                    }
                    return (
                      n.data.guideVersion && n.data.guideVersion > "3.2.0"
                        ? ((n.data.v3Update = 0),
                          console.log(
                            "Sorry,the version of V3.JS is too low, please upgrade!"
                          ))
                        : (n.data.v3Update = 1),
                      o(e, n.data),
                      Promise.resolve()
                    );
                  });
              },
              d = function (e, t) {
                return e.protocol + "static-" + e.area + ".vaptcha.net/" + t;
              },
              p = function (t) {
                var n = document.getElementsByTagName("head")[0],
                  a = document.getElementById("vaptcha_style");
                return new Promise(function (r) {
                  e(a)
                    ? ((a = document.createElement("link")),
                      o(a, {
                        rel: "stylesheet",
                        type: "text/css",
                        href: t,
                        id: "vaptcha_style",
                        onload: r,
                      }),
                      n && n.appendChild(a))
                    : r();
                });
              },
              h = function O(e) {
                var n = document.getElementsByTagName("head")[0],
                  a = document.querySelector("script[src='" + e + "']");
                return new Promise(function (r) {
                  if (t(a))
                    return void (a.loaded
                      ? r()
                      : setTimeout(function () {
                          return O(e).then(r);
                        }));
                  a = document.createElement("script");
                  var i = function () {
                    (a.readyState &&
                      "loaded" !== a.readyState &&
                      "complete" !== a.readyState) ||
                      (r(),
                      (a.loaded = !0),
                      (a.onload = null),
                      (a.onreadystatechange = null));
                  };
                  o(a, {
                    async: !0,
                    charset: "utf-8",
                    src: e,
                    onerror: function () {
                      return u("load sdk timeout");
                    },
                    onload: i,
                    onreadystatechange: i,
                  }),
                    n.appendChild(a);
                });
              },
              y = function (e) {
                var t = e.sdkName,
                  n = e.config,
                  a = d(n, "js/" + n.js_path);
                return h(a).then(function () {
                  var e = v(t);
                  return Promise.resolve(
                    new (0, window["_" + e + "Vaptcha"])(n)
                  );
                });
              },
              w = function (e, n) {
                var a = t(localStorage.getItem("vaptchaNetway")),
                  r = t(localStorage.getItem("vaptchaNetwayTime"))
                    ? localStorage.getItem("vaptchaNetwayTime")
                    : 0,
                  o = new Date().getTime() - r,
                  i =
                    (t(localStorage.getItem("vaptchaNetwayTime2")) &&
                      localStorage.getItem("vaptchaNetwayTime2"),
                    j - b),
                  c = void 0;
                if (
                  (i < 500 &&
                    !a &&
                    (localStorage.setItem("vaptchaNetway", e),
                    localStorage.setItem(
                      "vaptchaNetwayTime",
                      new Date().getTime()
                    ),
                    localStorage.setItem("vaptchaNetwayTime2", i)),
                  !a || i >= 500 || o > 864e5)
                ) {
                  e = e.toLowerCase();
                  var u = void 0;
                  u = "cn" == e ? "sea" : "cn";
                  var l = "api-" + u + ".vaptcha.net";
                  (I = new Date().getTime()),
                    S.staticConfig({
                      protocol: n.protocol,
                      url: l,
                      type: "/channel/",
                      id: n.vid,
                      waitTime: 5e3,
                    }).then(function (t) {
                      (N = new Date().getTime()),
                        (c = N - I),
                        i < c &&
                          (localStorage.setItem("vaptchaNetway", e),
                          localStorage.setItem(
                            "vaptchaNetwayTime",
                            new Date().getTime()
                          ),
                          localStorage.setItem("vaptchaNetwayTime2", i)),
                        c < i &&
                          (localStorage.setItem("vaptchaNetway", u),
                          localStorage.setItem(
                            "vaptchaNetwayTime",
                            new Date().getTime()
                          ),
                          localStorage.setItem("vaptchaNetwayTime2", c));
                    });
                }
              },
              A = function (e) {
                if ("auto" === e.lang || "" === e.lang) {
                  var o = r();
                  e.lang = o || "zh-CN";
                }
                (l = !0),
                  (e.https = !0),
                  (e.protocol = "https://"),
                  T.setConfig(e),
                  t(e.type) && (e.mode = e.type),
                  "embedded" === e.mode && (e.mode = "embed"),
                  !["embed", "popup", "invisible"].includes(e.mode) &&
                    (e.mode = "popup"),
                  t(e.mode) && (e.type = e.mode);
                var i = new g(e);
                if (
                  (i.addValidateRules({
                    elementOrSelector: function (t, r) {
                      if (
                        ("String" === c(e.container) &&
                          (e.container = document.querySelector(e.container)),
                        n(e.container) &&
                          a(e.container[0]) &&
                          (e.container = e.container[0]),
                        !a(e.container))
                      )
                        return r;
                    },
                  }),
                  i.add("vid", "required", "please configure vid"),
                  "invisible" !== e.mode &&
                    i.add(
                      "container",
                      "elementOrSelector",
                      "5004: please configure container with element or selector"
                    ),
                  i.validate())
                )
                  return f(e)
                    .then(function () {
                      var t = e.https
                          ? "css/theme_https." + e.css_version + ".css"
                          : "css/theme." + e.css_version + ".css",
                        n = d(e, t);
                      return p(n);
                    })
                    .then(function () {
                      var t = e.mode;
                      return (l = !1), y({ sdkName: t, config: e });
                    });
              };
            return function k(e) {
              return new Promise(function (t) {
                l
                  ? setTimeout(function () {
                      k(e).then(t);
                    }, 1e3)
                  : A(e).then(t);
              })["catch"](function (e) {
                return (l = !1), u(e), Promise.reject(e);
              });
            };
          })(),
          O = (function () {
            var e = function (e) {
                var n = e.getAttribute("data-config"),
                  a = {};
                if (t(n))
                  try {
                    a = JSON.parse(n);
                  } catch (r) {
                    u("dom config format error");
                  }
                return a;
              },
              n = function (e) {
                var n = e.getAttribute("data-vid");
                return t(n) ? { vid: n } : {};
              },
              a = function (e, n) {
                var a = Object.create(d);
                (a.container = e),
                  o(a, n),
                  t(a.vid) &&
                    A(a).then(function (e) {
                      e.renderTokenInput(), e.render();
                    });
              };
            return function () {
              for (
                var t = document.querySelectorAll("[data-vid]"),
                  r = document.querySelectorAll("[data-config]"),
                  o = 0;
                o < r.length;
                o++
              ) {
                var i = e(r[o]);
                a(r[o], i);
              }
              for (var c = 0; c < t.length; c++)
                if (!Array.prototype.includes.call(r, t[c])) {
                  var u = n(t[c]);
                  a(t[c], u);
                }
            };
          })();
        (window.onload = O),
          (window.vaptcha = function (e) {
            var t = Object.create(d);
            return (
              o(t, e),
              ("auto" === t.lang || "" === t.lang) && (t.lang = f() || "zh-CN"),
              A(t)
            );
          });
      })();
    </script>
  </head>

  <body>
    <div id="VAPTCHAContainer">
      <!-- 下面代码为预加载动画代码,仅供参考 -->
      <div class="VAPTCHA-init-main">
        <div class="VAPTCHA-init-loading">
          <a href="/" target="_blank">
            <svg
              xmlns="http://www.w3.org/2000/svg"
              xmlns:xlink="http://www.w3.org/1999/xlink"
              width="48px"
              height="60px"
              viewBox="0 0 24 30"
              style="
                enable-background: new 0 0 50 50;
                width: 14px;
                height: 14px;
                vertical-align: middle;
              "
              xml:space="preserve"
            >
              <rect x="0" y="9.22656" width="4" height="12.5469" fill="#CCCCCC">
                <animate
                  attributeName="height"
                  attributeType="XML"
                  values="5;21;5"
                  begin="0s"
                  dur="0.6s"
                  repeatCount="indefinite"
                ></animate>
                <animate
                  attributeName="y"
                  attributeType="XML"
                  values="13; 5; 13"
                  begin="0s"
                  dur="0.6s"
                  repeatCount="indefinite"
                ></animate>
              </rect>
              <rect
                x="10"
                y="5.22656"
                width="4"
                height="20.5469"
                fill="#CCCCCC"
              >
                <animate
                  attributeName="height"
                  attributeType="XML"
                  values="5;21;5"
                  begin="0.15s"
                  dur="0.6s"
                  repeatCount="indefinite"
                ></animate>
                <animate
                  attributeName="y"
                  attributeType="XML"
                  values="13; 5; 13"
                  begin="0.15s"
                  dur="0.6s"
                  repeatCount="indefinite"
                ></animate>
              </rect>
              <rect
                x="20"
                y="8.77344"
                width="4"
                height="13.4531"
                fill="#CCCCCC"
              >
                <animate
                  attributeName="height"
                  attributeType="XML"
                  values="5;21;5"
                  begin="0.3s"
                  dur="0.6s"
                  repeatCount="indefinite"
                ></animate>
                <animate
                  attributeName="y"
                  attributeType="XML"
                  values="13; 5; 13"
                  begin="0.3s"
                  dur="0.6s"
                  repeatCount="indefinite"
                ></animate>
              </rect>
            </svg>
          </a>
          <span class="VAPTCHA-text">Vaptcha Initializing...</span>
        </div>
      </div>
    </div>
    <script>
      function appCallMethod(key, value) {
        window[key] = value;
      }
      function callAppMethod(method, data = {}) {
        try {
          lh.postMessage(
            JSON.stringify({
              method,
              data,
            })
          );
        } catch (error) {}
      }
    </script>

    <script>
      var serverToken = {};
      var data = {};
      function getServerToken() {
        return serverToken;
      }
      function getData() {
        return data;
      }

      vaptcha({
        vid: "65ddaa64d3784602950e7b12",
        mode: "click",
        scene: "1",
        container: "#VAPTCHAContainer",
        area: "auto",
        lang: "en",
      }).then(function (VAPTCHAObj) {
        // 将VAPTCHA验证实例保存到局部变量中
        obj = VAPTCHAObj;

        // 渲染验证组件
        VAPTCHAObj.render();

        // 验证成功进行后续操作
        VAPTCHAObj.listen("pass", function () {
          serverToken = VAPTCHAObj.getServerToken();
          data = {
            server: serverToken.server,
            token: serverToken.token,
            scene: (window["scene"] ?? 1) * 1,
          };
          callAppMethod("getServerToken", data);
        });
      });
    </script>
  </body>
</html>

最后只需要在接口拦截器中 判断错误状态码 然后主动弹起人机验证

相关推荐
️ 邪神12 小时前
【Android、IOS、Flutter、鸿蒙、ReactNative 】文本点击事件
flutter·ios·鸿蒙·reactnative·anroid
️ 邪神13 小时前
【Android、IOS、Flutter、鸿蒙、ReactNative 】文本Text显示
flutter·ios·鸿蒙·reactnative·anroid
iFlyCai15 小时前
Flutter中有趣的级联语法
flutter
恋猫de小郭15 小时前
Flutter 小技巧之 Shader 实现酷炫的粒子动画
flutter
hello world smile18 小时前
Dart中List API用法大全
flutter·list·dart
lqj_本人1 天前
Flutter&鸿蒙next 使用 BLoC 模式进行状态管理详解
flutter·华为·harmonyos
Miketutu1 天前
flutter 项目初建碰到的控制台报错无法启动问题
flutter
lqj_本人1 天前
flutter&鸿蒙next 使用 InheritedWidget 实现跨 Widget 传递状态
flutter·华为·harmonyos
氤氲息1 天前
flutter 发版的时候设置版本号
flutter
潘敬1 天前
flutter 语法糖库 flutter_magic 发布 1.0.1
开发语言·前端·javascript·flutter·typescript