行为式验证码是一种全新人机验证方式，能够高效拦截机器行为，构筑业务安全第一道防线。

目前行为式验证码支持的类型有：[智能无感知](https://dun.163.com/trial/sense)、[滑动拼图](https://dun.163.com/trial/jigsaw)、[文字点选](https://dun.163.com/trial/picture-click)、[图标点选](https://dun.163.com/trial/icon-click)、[推理拼图](https://dun.163.com/trial/inference)、[短信上行](https://dun.163.com/trial/sms)、[语序点选](https://dun.163.com/trial/word-order)、[空间推理](https://dun.163.com/trial/space-inference)、[障碍躲避](https://dun.163.com/trial/avoid)、
[语序选词](https://dun.163.com/trial/word-group)、语音验证码。

验证码类型和验证码ID（captchaId）绑定，可以登录[管理后台](https://dun.163.com/dashboard#/login/)灵活切换。

![验证码类型](https://nos.netease.com/yidun/4479496b96530a851f485eb99b7e93ae.png)

## 平台支持

行为式验证码兼容市场主流浏览器，具体版本见下表：

|浏览器|最低支持版本|
|----|----|
| IE | 7 |
| Edge | 44 |
| Chrome | 44 |
| Firefox | 71 |
| Mac Safari | 13 |
| UC | 9 |
| Android Chrome | 7 |
| 微信浏览器 | 7.1 |
| Opera | 8 |
| iOS Safari | 12 |

## SDK 接入

### 包含降级方案接入（强烈推荐）

如果对稳定性有更高的要求，推荐使用降级方案中自定义降级逻辑，降级方案有如下优势：

多域名冗余：多个域名备份，主域名请求失败会切换其他域名重试。

服务故障兜底：若易盾整体服务故障，会有兜底方案通过请求。

降级回调：降级脚本内部达到一定重试次数失败后有回调通知，建议其他方式降级验证处理。

详细用法可以进一步咨询技术支持。

* 将[降级脚本](https://github.com/yidun/captcha-java-demo/blob/master/src/main/webapp/index.js)下载至本地项目中；
* 在页面中引入上面的脚本，示例如下：
* Script 引入

```html
<script src="/path/yidun-captcha.js"></script>
```

* 模块引入

```js
import initNECaptchaWithFallback from '/path/yidun-captcha'
```

**注释：** 当行为验证码所需静态资源或者接口不可用时，且请求验证码失败次数达到阈值（默认为 3 次，可根据需求配置），最后一次会触发降级逻辑，降级表现为：

Web 端提示 `前方拥堵，已自动跳过验证`（智能无感知无该提示），验证码直接验证通过，用户可进行后续业务操作，免受行为验证码的影响。

页面接入示例

```html
<!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">
  <title>验证码接入示例</title>
  <script src="/path/yidun-captcha.js"></script>
</head>

<body>
  <div>
    <div id="captcha"></div>
    <button id="login">登录</button>
  </div>
  <script>
    (function () {
      var captchaIns;
      initNECaptchaWithFallback({
        element: '#captcha',
        captchaId: '<CAPTCHA_ID>',
        width: '320px',
        mode: 'popup',
        apiVersion: 2,
        popupStyles: {
          position: 'fixed',
          top: '20%'
        },
        onVerify: function (err, data) {
          /**
           * 第一个参数是err（Error的实例），验证失败才有err对象
           * 第二个参数是data对象，验证成功后的相关信息，data数据结构为key-value，如下：
           * {
           *   validate: 'xxxxx' // 二次验证信息
           * }
           **/
          if (err) return // 当验证失败时，内部会自动refresh方法，无需手动再调用一次

          // 点击登录按钮后可调用服务端接口，以下为伪代码，仅作示例用
          ajax('/login', {
            username: $('#username').val(),
            password: $('#password').val(),
            validate: data.validate
          }, function onsuccess(data) {
            // if (loginFail) {
            // 密码错误等原因导致登录失败，业务上若需要刷新验证码，可调用refresh方法
            //   captchaIns && captchaIns.refresh()
            // } else {
            // 登录成功后，相关处理逻辑
            // }
          })
        }
      }, function onload(instance) {
        captchaIns = instance
      }, function onerror(err) {
        console.warn(err)
      })

      var loginBtn = document.getElementById('login')
      loginBtn.addEventListener('click', function () {
        captchaIns && captchaIns.verify()
      })
    })();
  </script>
</body>

</html>
```

更多使用场景，请参见[DEMO](https://support.dun.163.com/captcha-demo/float)。

查看主流框架的示例，请参见[DEMO](https://github.com/yidun/captcha-demos)。

### 不含宕机方案接入

通过 `<script>` 标签引入文件

```html
<!-- https示例如下: -->
<script src="https://cstaticdun.126.net/load.min.js?t=<MINUTE_TIMESTAMP>"></script>

<!-- http示例如下: -->
<script src="http://cstaticdun.126.net/load.min.js?t=<MINUTE_TIMESTAMP>"></script>

<!-- 地址中的t参数强烈建议设置为分钟级别时间戳, 避免该文件被浏览器长时间缓存 -->
```
**ps：**建议业务侧直接使用远程 load.min.js 文件（可以确保使用最新版本），避免下载到项目本地。这将有助于我们后续更新策略的顺利执行。****

页面接入示例

```html
<!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">
  <title>验证码接入示例</title>
  <script src="https://cstaticdun.126.net/load.min.js"></script>
</head>

<body>
  <div>
    <div id="captcha"></div>
    <button id="login">登录</button>
  </div>
  <script>
    (function () {
      var captchaIns;
      initNECaptcha({
        element: '#captcha',//popup 模式下，element 可以为空或者document.body
        captchaId: '<CAPTCHA_ID>',
        width: '320px',
        mode: 'popup',
        apiVersion: 2,
        popupStyles: {
          position: 'fixed',
          top: '20%'
        },
        onVerify: function (err, data) {
          /**
           * 第一个参数是err（Error的实例），验证失败才有err对象
           * 第二个参数是data对象，验证成功后的相关信息，data数据结构为key-value，如下：
           * {
           *   validate: 'xxxxx' // 二次验证信息
           * }
           **/
          if (err) return // 当验证失败时，内部会自动refresh方法，无需手动再调用一次

          // 点击登录按钮后可调用服务端接口，以下为伪代码，仅作示例用
          ajax('/login', {
            username: $('#username').val(),
            password: $('#password').val(),
            validate: data.validate
          }, function onsuccess(data) {
            // if (loginFail) {
            // 密码错误等原因导致登录失败，业务上若需要刷新验证码，可调用refresh方法
            //   captchaIns && captchaIns.refresh()
            // } else {
            // 登录成功后，相关处理逻辑
            // }
          })
        }
      }, function onload(instance) {
        captchaIns = instance
      }, function onerror(err) {
        console.warn(err)
      })

      var loginBtn = document.getElementById('login')
      loginBtn.addEventListener('click', function () {
        captchaIns && captchaIns.verify()
      })
    })();
  </script>
</body>

</html>
```

## 方法说明

以下示例都是以不含宕机方案接入，若接入方式为含宕机方案，只需将 `initNECaptcha` 替换成 `initNECaptchaWithFallback` 即可。

### 初始化

#### 示例代码

```js
initNECaptcha(options, function onload (instance) {}, function onerror (error) {})
```

#### 参数说明：

|    参数   |          类型        |是否必填|默认值|描述|
|-----------|---------------------|-------|------|------------|
|captchaId |String  |是 |无 |验证码业务ID |
|mode |String | 否 | PC 端默认 `"float"`，移动端默认 `"popup"` | 验证码模式，有三种模式可选：`"float"`（触发式）、`"embed"`（嵌入式）、`"popup"`（弹出式），其中智能无感知类型不支持 `"embed"` 模式。[示例demo](http://support.dun.163.com/captcha-demo/float) |
|element |String/HTMLElement|是 |无 |容器元素或容器元素选择器，popup 模式下，element 可以为空或者document.body|
|width | Number/String | 否 |'auto' |验证按钮宽度，推荐使用宽度 260px-400px。类型为 String 时，支持后缀 `px`、`rem`、`%`，类型为 Number 时，内部会将其转换成 px 单位的值。当值为 `"auto"` 时，其宽度与容器元素宽度一致。mode 为 `"popup"` 时，百分比单位无效 |
|apiVersion|Number|是|无|SDK 版本，推荐设置为 2|
|protocol |String |否 |与用户网站的网络协议一致 | 验证码传输数据使用的网络协议，可选：`"http"`、`"https"` |
|lang |String |否 |'zh-CN' |验证码语言选项。支持 `"zh-CN"`、`"zh-TW"`、`"en"`等具体参考文档下方[附录-lang枚举](#2.2)|
|timeout|Number|否| 6000ms | 内部每个请求的超时时间 |
|closeEnable |Boolean |否 |false |是否由产品方控制验证码弹框关闭|
|feedbackEnable |Boolean |否 | true|是否开显示用户反馈入口（底图右上角问号图标）|
|feedbackUrl |String |否 | 无|用户自定义反馈链接url|
|ipv6 |Boolean |否 | false|是否支持 ipv6 网络，如果设置为 true ，验证码内部的所有请求都首先使用 ipv6 域名进行|
|extraData |String/Function | 否|无 |如果需要在 check 阶段透传业务数据，可以使用 extraData 配置，支持字符串和函数，函数可以解决动态数据问题。当调用二次校验结果接口时，会原样返回该字段，详见[后端响应参数](http://support.dun.163.com/documents/15588062143475712?docId=69218161355051008) |
|maxVerification |Number |否 |5 |最大验证码失败次数，到达上限后会重置验证码，开始新的验证流程 |
|defaultFallback |Boolean |否 |true |使用含宕机方案接入有效。是否启用默认降级方案，若设置 `false`，可搭配onFallback自定义降级方案 |
|errorFallbackCount |Number |否 |3 |使用含宕机方案接入有效。触发降级的最大错误次数，当超过这个错误次数时，触发降级；降级产生的二次验证信息默认无法通过二次验证。大面积发生降级时，请联系客服 |
|enableAutoFocus |Boolean |否 |无 |验证码弹框弹起时是否自动聚焦至弹框元素 |
|onFallback |Function |否 |无 |使用含宕机方案接入有效。验证码触发降级的回调，仅 defaultFallback 为 `false` 时有效 |
|onReady |Function |否 |无 |验证码所有工作准备就绪的回调函数 |
|onVerify |Function |否 |无 |验证码一次验证结束回调函数 |
|onOpen |Function |否 |无 |验证码弹框弹出前的回调 |
|onClose |Function |否 |无 |验证码弹框关闭后的回调 |
|apiServer |string/string[] |否 |易盾默认域名 |用于指定验证码接口域名，私有化部署、域名转发方式接入需要使用，常规接入不需要设置 |
|staticServer |string/string[] |否 |易盾默认域名 |用于指定验证码静态资源域名，私有化部署、域名转发方式接入需要使用，常规接入不需要设置 |
|customStyles|Object|否|无|**高级定义功能-主界面自定义样式**，请咨询[客服](https://dun.163.com/kefu)|
|popupStyles|Object|否|无|**高级定义功能-弹框界面自定义样式**，请咨询[客服](https://dun.163.com/kefu)|
|customTexts|Object|否|无|**高级定义功能-文案自定义**，请咨询[客服](https://dun.163.com/kefu)|

#### 回调参数说明：

* onload(instance) 回调参数说明

| 参数 | 类型 | 描述 |
|-----|------|------|
|instance |Object |初始化成功后的验证码实例 |

* onerror(error) 回调参数说明

| 参数 | 类型 | 描述 |
|------|-----|------|
|error |Error|初始化失败的错误信息,详细错误码请参考[附录-错误码](#2.1) |

* onVerify(err, data) 回调参数说明

| 参数 | 类型 | 描述|
|------|------|----|
|err   |Error |验证失败的错误信息 |
|data  |Object |验证成功后的信息，格式为`{ validate: '' }`，其中 validate 为二次验证信息 |

### 显示验证码（可选）

当 `mode` 配置成 `popup` 时，需要调用 `verify` 显示验证码；否则无需调用，用户直接操作界面进行验证

#### 代码说明

```js
instance.verify()  // instance 为行为式验证码实例
```

### 关闭验证码（可选）

当 `closeEnable` 设置为 `true` 时，产品方可获得主动关闭验证码弹框的权限。若需要关闭验证码弹框，可以调用 `close` 方法。

#### 代码说明

```js
instance.close() // instance 为行为式验证码实例
```

### 刷新验证码（可选）

当验证成功后，若产品方有场景需要刷新验证码，重新进行验证时，可以调用 `refresh` 方法：

#### 代码说明

```js
instance.refresh() // instance 为行为式验证码实例
```

**注意：请不要在验证失败时调用，失败时验证码会自动刷新。**

### 销毁验证码（可选）

当业务不再需要验证码，需要销毁验证码时，可以调用 `destroy` 方法：

#### 代码说明

```js
instance.destroy() // instance 为行为式验证码实例
```

#### 特别说明

`onload` 和 `onReady` 的区别：

`onload` 和 `onReady` 触发时，均会返回验证码的实例，即传入的第一个参数；二者触发时机有所不同：

`onload` 触发时，初始化函数结束和完成实例的生成，注意这并不代表验证码是可用的（比如验证码相关背景图片和信息并没有加载），此方法只触发一次；

`onReady` 触发时，说明验证码准备就绪（比如背景图片等信息均已加载），`onReady`只触发一次；

</md-attention>

#### mode 模式演示

* popup，验证码交互模式为弹出式，效果见 [popup演示DEMO](https://support.dun.163.com/captcha-demo/popup)
* float，验证码交互模式为触发式，效果见 [float演示DEMO](https://support.dun.163.com/captcha-demo/float)
* embed，验证码交互模式为嵌入式，效果见 [embed演示DEMO](https://support.dun.163.com/captcha-demo/embed)

#### closeEnable

当 `closeEnable` 为 `true` 时，将由产品方控制验证码弹框的关闭时机，验证码不会主动关闭弹框，且弹框右上角的关闭按钮隐藏。当业务方需要关闭验证码弹框时，可以调用 `close` 方法。使用示例如下：

```html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>验证码示例-closeEnable</title>
  <script src="https://cstaticdun.126.net/load.min.js"></script>
</head>

<body>
  <button id="j-popup">点击弹出验证码</button>
  <div id="captcha"></div>
  <script>
    var captchaIns;
    initNECaptcha({
      element: '#captcha',
      captchaId: '<CAPTCHA_ID>',
      mode: 'popup',
      width: '320px',
      closeEnable: true,
      apiVersion: 2,
      popupStyles: {
        position: 'fixed',
        top: '20%'
      },
      onClose: function () {
        // 弹出关闭结束后将会触发该函数
      },
      onVerify: function (err, data) {
        if (!err) {
          // 验证成功后，调用 close 方法关闭弹框
          captchaIns.close()
          // TODO: 验证成功后继续进行业务逻辑
        }
      }
    }, function (instance) {
      // 初始化成功后得到验证实例 instance，可以调用实例的方法
      captchaIns = instance
    }, function (err) {
      // 初始化失败后触发该函数，err 对象描述当前错误信息
    })

    // 监听 button 的点击事件，弹出验证码
    document.getElementById('j-popup').addEventListener('click', function () {
      captchaIns && captchaIns.verify()
    })
  </script>
</body>

</html>
```

#### form 表单进行二次验证

通常业务方可以在 `onVerify` 中获取到二次校验信息后进行二次验证；但若产品方是使用表单提交的方式进行请求，可以参考如下示例：

```html
<!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>
  <script src="https://cstaticdun.126.net/load.min.js?t=201903281201"></script>
</head>

<body>
  <form action="/login" method="post">
    <input type="text" name="username" placeholder="用户名">
    <input type="password" name="password" placeholder="密码">
    <input type="hidden" name="captchaId" value="从易盾申请的captchaId">
    <div id="captcha"></div> <!-- 验证码容器元素 -->
    <button type="submit">登录</button>
  </form>
  <script>
    initNECaptcha({
      captchaId: '<CAPTCHA_ID>',
      element: '#captcha',
      width: '320px',
      apiVersion: 2,
      popupStyles: {
        position: 'fixed',
        top: '20%'
      }
    }, function onload(instance) {
      // 初始化成功后，用户输入对应用户名和密码，以及完成验证后，直接点击登录按钮即可
    }, function onerror(err) {
      // 验证码初始化失败处理逻辑，例如：提示用户点击按钮重新初始化
    })
  </script>
</body>

</html>
```

## 附录

#### <a id=2.1>错误码</a>

| code | 含义 |
|------|------|
| 501  |请求失败，包括网络原因等，加载阶段频繁触发qps超限也会返回此code |
| 502  |请求脚本资源失败 |
| 432  |非法业务ID，包含业务到期等|
| 1000 | 未知错误 |
| 300  |校验未通过，包含轨迹错误等|
| 200  |校验未通过，是因为业务错误，包含qps超限等 |

#### <a id=2.2> lang 可选项</a>

验证码支持以下语言：

|语言标识|语种|
|----|----|
|zh-CN|简体中文|
|zh-TW|台湾繁体中文|
|zh-HK|香港繁体中文|
|en-US|美式英文|
|en-GB|英式英文|
|es|西班牙语（欧洲）|
|pt|葡萄牙语（欧洲）|
|fr|法语|
|de|德语|
|ru|俄语|
|it|意大利语|
|ja|日语|
|es-la|拉美西语|
|pt-br|巴西葡语|
|sv|瑞典语|
|no|挪威语|
|da|丹麦语|
|cs|捷克语|
|hu|匈牙利语|
|sk|斯洛伐克语|
|pl|波兰语|
|ro|罗马尼亚语|
|el|希腊语|
|sr|塞尔维亚语（拉丁文）|
|bs|波斯尼亚语|
|mk|马其顿语|
|bg|保加利亚语|
|fi|芬兰语|
|et|爱沙尼亚语|
|lv|拉脱维亚语|
|lt|立陶宛语|
|sl|斯洛文尼亚语|
|hr|克罗地亚语|
|uk|乌克兰语|
|tr|土耳其语|
|vi|越南语|
|id|印尼语|
|ar|阿拉伯语|
|fa|波斯语|
|nl|荷兰语|
|th|泰语|
|ms|马来西亚语|
|ca|加泰罗尼亚语|
|hi|印地语|
|my|缅甸语|
|ko|韩语|
|he|希伯来语|
|gl|加利西亚语|
|eu|巴斯克语|
|ka|格鲁吉亚语|
|az|阿塞拜疆语|
|uz|乌孜别克语|
|km|高棉语|
|si|僧伽罗语|
|ur|乌尔都语|
|bo|藏语|
|be|白俄罗斯语|
|kk|哈萨克语（西里尔文）|
|bn|孟加拉语|
|lo|老挝语|
|fil|菲律宾语|
|jv|爪哇语|
|ne|尼泊尔语|
|sw|斯瓦西里语|
|mi|毛利语|
|am|阿姆哈拉语|
|te|泰卢固语|
|mr|马拉地语|
|ta|泰米尔语|
|gu|古吉拉特语|
|kn|卡纳达语|
|ml|马来亚拉姆语|
|or|欧里亚语|
|pa|旁遮普语|
|as|阿萨姆语|
|mai|迈蒂利语|
|mn|蒙古语（西里尔文）|
|ug|维吾尔语|