网易云盾函数封装

285 阅读3分钟

网易云盾使用

日常使用app、web网站登录时,经常会遇到验证码校验问题,传统的验证是让用户输入页面上展示的文本,不仅费时而且对于交互而言并不算友好。在开发过程中使用了网易云盾图形校验,让用户通过滑动的方式来实现安全认证。

1. 定义CstaticdunValidate类

首先我们定义一个工具类来初始化网易云盾

/**
 * 1、默认网易云盾智能免验证
 * 2、初始化可开启网易云盾非智能免验证
 * 在发送短信验证码的场景下使用2,其他的使用1。启用参数:force,布尔值
 * 两种验证码唤起方式1:verify(),2: popUp()
 */
class CstaticdunValidate {
  constructor(options, onInitCallback, onErrorCallback, force = false) {
    this.options = options;
    this.onInitCallback = onInitCallback;
    this.onErrorCallback = onErrorCallback;
    this.force = force;
    this.defaultOptions = {
      element: '#captcha', // DOM元素
      width: 'computed', // 验证按钮宽度
      // protocol: 'http', // 验证码传输数据使用的网络协议,默认使用网站协议
      lang: 'zh-CN', // 验证码语言选项
      onReady: function (instance) {
        // 验证码一切准备就绪,此时可以正常使用验证码的功能
      },
      onVerify: function (err, data) {
        // 验证码验证结束回调
      },
    };
    this.init();
  }
  init() {
    let extendOptions;
    const _this = this;
    if (this.force) { // 强制验证
      extendOptions = {
        captchaId: 'foo', // 从网易云盾申请captchaId
        mode: 'popup' // 从页面上弹出
      }
    } else { // 智能验证
      extendOptions = {
        captchaId: 'bar',
        mode: 'bind', // 无感知认证
      }
    }
    const formatOptions = Object.assign(this.defaultOptions, this.options, extendOptions);
    let computedOptions = null;
    if (formatOptions.width === 'computed') {
      computedOptions = this.computeWidth(formatOptions);
    }
    this.options = computedOptions || formatOptions;
    window.initNECaptcha && window.initNECaptcha(formatOptions, function onload (instance) {
      // 初始化成功
      _this.cstaticdun = instance;
      _this.onInitCallback && _this.onInitCallback(instance);
    }, function onerror (err) {
      // 验证码初始化失败处理逻辑
      _this.onErrorCallback && _this.onErrorCallback(err);
    })
  }
  computeWidth(formatOptions) {
    formatOptions.width = '8rem';
    return formatOptions;
  }
}

export default CstaticdunValidate;

1.1 mode

上面的代码中我们可以看到定义mode 官方文档中给了几种mode使用方式:

  • 常用验证码
    • 触发式(float)
    • 弹出式(popup)
    • 嵌入式(embed)
  • 无感知模式
    • bind模式(bind)

具体使用官方文档中有给出案例

1.2 onVerify(err, data)

当调用onVerify是,会传入两个参数

  • err(Error实例),验证失败才会有error对象
  • data: 验证成功后的相关信息,data数据结构为key-value格式

2. 定义MontNECaptcha函数

该函数用于在页面渲染时,加载网易云盾的js并进行方法初始化

/**
 * 挂载网易云盾插件
 * @param callback
 */
const montNECaptcha = (callback) => {
  // 如果已经初始化则直接回调成功
  // @ts-ignore
  if (window.initNECaptcha) {
    // @ts-ignore
    callback && callback(!!window.initNECaptcha);
    return;
  }
  // @ts-ignore
  let captcha;
  // 检查是否已经存在元素节点,防止重复添加
  const elements = document.querySelector('#NECaptchaJS');
  // @ts-ignore
  if (!elements) {
    captcha = document.createElement('script');
    captcha.id = 'NECaptchaJS';
    captcha.type = 'text/javascript';
    captcha.src = 'https://cstaticdun.126.net/load.min.js' + '?t=' + getTimestamp(10 * 60 * 1000) // 时长1分钟,建议时长分钟级别
	
    const head = document.head || document.getElementsByTagName('head')[0]
    head.appendChild(captcha)
  } else {
    captcha = elements;
  }

  // 记录元素原本的onload函数
  let preOnLoad = captcha.onload;
  captcha.onload = function () {
    if (preOnLoad) {
      // @ts-ignore
      preOnLoad(!!window.initNECaptcha);
      preOnLoad = null;
    }
    // @ts-ignore
    callback && callback(!!window.initNECaptcha)
    callback = null;
  }
};

function getTimestamp (msec) {
  msec = !msec && msec !== 0 ? msec : 1
  return parseInt((new Date()).valueOf() / msec + '', 10)
}

export default plugYunDun

上面的代码中captcha.src = 'https://cstaticdun.126.net/load.min.js' 在页面的<head>中引入网易云盾的包,后面的?t=是用来记录时间戳,清除缓存

3. 定义useCstaticdun函数

对以上的代码进行一层封装,这样可以在页面调用时减少代码量

import { useEffect, useRef } from 'react'
import plugYunDun from '@/utils/yunDunUtil'
import StaticdunValidate from '@/utils/cstaticdunValidate'

export interface ICstaticdunProps {
  onYunDunVerify: (err, data) => void
}

export default function useCstaticdun(props: ICstaticdunProps) {
  const cstaticdunRef = useRef(undefined as any)
  const { onYunDunVerify } = props

  useEffect(() => {
    // 初始化网易云盾
    plugYunDun((success) => success && initCstaticdunValidate())
  }, [])

  /**
   * 网易云盾绑定
   */
  const initCstaticdunValidate = () => {
    new StaticdunValidate(
      {
        onVerify: onYunDunVerify,
      },
      (instance) => {
        console.warn('NECaptcha has inited')
        cstaticdunRef.current = instance
      },
      (err) => {
        console.log(err)
      },
    )
  }

  const refresh = (callback?) => {
    cstaticdunRef.current.refresh()
    setTimeout(() => {
      cstaticdunRef.current && cstaticdunRef.current.verify()
      callback && callback()
    }, 250)
  }

  return {
    cstaticdunRef,
    cstaticdunRefresh: refresh,
  }
}

4. 页面中使用

const onYunDunVerify = (err, data) => {
    if (!err) {
        const params = {
            policyIdEn,
            captchaId: cstaticdunRef.current.captchaId,
            validate: data.validate,
            simpleCardNo: idCardRef.current
        }

        AuthRequest.certificateVerification(params).then((res) => {
            const result = plainToClass(BaseResponse, res)
             // do something
        }).catch((res: BaseResponse) => {
            // do something
        })
    }
}

const {cstaticdunRef, cstaticdunRefresh} = useCstaticdun({onYunDunVerify})