动态生成旋转图片

520 阅读5分钟

简介

大家好,我是chenms

前段时间我们公司获取手机号码的接口老是被人恶意刷,之前我们是通过动态图片去做的,如下图

image.png

这个是可以通过特殊工具直接获取图形里面的内容的,后面我们领导给我们出了一个方案就是通过图片旋转来解决这个问题,如下图

image.png

这个也不是简单的通过前端特效来完成的,这个是需要通过调用后端接口然后返回相应的图片,用户再把图片转正才能获取手机验证的,这样子就可以避免被恶意刷手机验证了,具体我们找了一个前后端都有的开源库 gitee.com/isszz/rotat… 我们项目使用vue3开发的,由于这个开源库的开发时间比较早,只支持vue2,所以我就把vue2相关的逻辑转成vue3,并把它发布到npm上

插件地址:www.npmjs.com/package/che…

希望可以帮到有需要用这个库而且又是用vue3开发的前端同学

使用

  1. 安装
npm i chenms-routate-captcha
  1. 参数介绍

options

这是一个对象,里面有若干个参数配置,具体如下

属性说明类型默认值
theme设置加载动态图片的颜色string#07f
desc设置滑块里的文案string滑动将图片转正
successClose验证成功后页面关闭时间(单位:ms)number1500
timerProgressBar验证成功后关闭时是否显示进度条booleantrue
timerProgressBarColor验证成功后关闭时是否显示进度条的背景颜色设置stringrgba(0, 0, 0, 0.1)
successCode接口请求成功的成功码number1
isShowCloseIcon是否显示关闭按钮小图标booleanfalse
request获取图片信息的ajax请求字段object-

特别说明

successClose这是属性成功之后的成功码,如下图所示,不要填错了

image.png

request

这是一个对象,里面有若干个参数配置,具体如下

属性说明类型默认值
info获取验证码信息function-
check验证旋转角度function-
img交换图片function-

示例

const captchaOptions = ref({
  theme: '#07f',
  desc: '滑动将图片转正',
  successClose: 1500, // 验证成功后页面关闭时间
  timerProgressBar: true, // 验证成功后关闭时是否显示进度条
  timerProgressBarColor: 'rgba(0, 0, 0, 0.2)', // 验证成功后关闭时是否显示进度条的背景颜色设置
  successCode: 1, // 接口请求成功的成功码
  isShowCloseIcon: false, // 是否显示关闭按钮
  request: {
    // 获取验证码信息
    info: (callback) => {
      getJSON(
        'axios的baseURL' + '获取选择图片信息接口(你们后端提供)',
        null,
        function (res, xhr) {
          if (xhr.status != 200) {
            // 报错提示,换成你们自己的提示相关的api
            $message.warn('系统出错:' + res.statusCode + ',请关闭重试!')
            return false
          }
           // 第二个参数传递从header中获取的token, 如果嫌麻烦, 可以在res内返回token
           callback(res.code, res.data.token, res.data.str)
        }
      )
    },
    // 验证, angle用户旋转角度
    check: (angle, token, callback) => {
      // 将token设置好, 数据验证时传递给后端
      // 当然这里的数据请求只作为参考, 实际使用中以你的数据请求组件方式为准
      getJSON(
        'axios的baseURL' + '验证旋转图片接口(你们后端提供)',
        { angle: angle, token }, // 这个token是我们后端要求放在请求参数这里的,具体看你们后端怎么要求
        function (res, xhr) {
          if (xhr.status != 200) {
          // 报错提示,换成你们自己的提示相关的api
            $message.warn('系统出错:' + res.statusCode + ',请关闭重试!')
            return false
          }
          callback(res.code)
        }
      )
    },
    // 交换图片
    img: (id) => {
      return 'axios的baseURL' + '获取图片(你们后端提供)' + '?id=' + id
    }
  }
})

// ajax请求, 这里只做演示, 请使用自己项目中的
const getJSON = (url, data, callback) => {
  let params = ''
  if (data && typeof data == 'object') {
    params = Object.keys(data)
      .map(function (key) {
        return encodeURIComponent(key) + '=' + encodeURIComponent(data[key])
      })
      .join('&')

    url = url + ((url.indexOf('?') == -1 ? '?' : '&') + params)
  }

  let xhr,
    formData = null
  xhr = new XMLHttpRequest()
  xhr.withCredentials = false

  xhr.open('GET', url)
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
  xhr.onload = function () {
    if (xhr.status != 200) {
      return
    }

    try {
      let res = JSON.parse(xhr.responseText) || null
      if (!res) {
        return
      }
      callback(res, xhr)
    } catch (e) {
      return
    }
  }

  xhr.send(formData)
}

特别注意

上面示例代码中你们后端给你们提供的三个接口前面一定要加axios的baseURL,不然图片会出不来

size

这是一个对象,里面有若干个参数配置,具体如下

属性说明类型默认值
width动态旋转图片验证的布局宽度number305
img里面图片的宽度number152
control滑动条布局的宽度number275
imgMargin图片上下的margin值number10

具体示例

由于我们的项目需要用到多个这样的旋转图片验证,所以我就再做了一层封装,把公共的配置封装在组件里,这样在需要用到的页面调用就会显得很简洁了

封装的组件代码

<template>
  <cms-routate-captcha
    :options="{ ...captchaOptions, ...options }"
    :size="size"
    v-if="captchaShow"
    @close="close"
    @complete="captchaComplete"
    @success="captchaSuccess"
    @fail="captchaFail"
  />
</template>

<script setup>
import cmsRoutateCaptcha from 'chenms-routate-captcha/index.vue'
import { message as $message } from 'ant-design-vue'
import { getEnvironmentConfig } from '@/hooks/useTool'
const props = defineProps({
  options: {
    type: Object,
    default: () => {}
  },
  captchaShow: {
    type: Boolean,
    default: false
  }
})

const emit = defineEmits(['update:captchaShow', 'captchaFail', 'captchaSuccess', 'captchaComplete'])

const captchaShow = computed({
  get() {
    return props.captchaShow
  },
  set(val) {
    emit('update:captchaShow', val)
  }
})

// 图形验证码相关的 start
const captchaOptions = ref({
  theme: '#07f',
  desc: '滑动将图片转正',
  successClose: 1500, // 验证成功后页面关闭时间
  timerProgressBar: true, // 验证成功后关闭时是否显示进度条
  timerProgressBarColor: 'rgba(0, 0, 0, 0.2)', // 验证成功后关闭时是否显示进度条的背景颜色设置
  successCode: 1, // 接口请求成功的成功码
  isShowCloseIcon: false, // 是否显示关闭按钮
  request: {
    // 获取验证码信息
    info: (callback) => {
      getJSON(
        getEnvironmentConfig().VITE_BASE_API + '/v1/captcha/getRotateCaptcha',
        null,
        function (res, xhr) {
          if (xhr.status != 200) {
            $message.warn('系统出错:' + res.statusCode + ',请关闭重试!')
            return false
          }
          // 第二个参数传递从header中获取的token, 如果嫌麻烦, 可以在res内返回token
          callback(res.code, res.data.token, res.data.str)
        }
      )
    },
    // 验证, angle用户旋转角度
    check: (angle, token, callback) => {
      // 将token设置好, 数据验证时传递给后端
      // 当然这里的数据请求只作为参考, 实际使用中以你的数据请求组件方式为准
      getJSON(
        getEnvironmentConfig().VITE_BASE_API + '/v1/captcha/checkRotateCaptcha',
        { angle: angle, token },
        function (res, xhr) {
          if (xhr.status != 200) {
            $message.warn('系统出错:' + res.statusCode + ',请关闭重试!')
            return false
          }
          callback(res.code)
        }
      )
    },
    // 交换图片
    img: (id) => {
      return getEnvironmentConfig().VITE_BASE_API + '/v1/captcha/getRotateCaptchaImg?id=' + id
    }
  }
})

const size = ref({
  // 动态旋转图片验证的布局宽度
  width: 250,
  // 里面图片的宽度
  img: 200,
  // 滑动条布局的宽度
  control: 220,
  // 图片上下的margin值
  imgMargin: 20
})

// ajax请求, 这里只做演示, 请使用自己项目中的
const getJSON = (url, data, callback) => {
  let params = ''
  if (data && typeof data == 'object') {
    params = Object.keys(data)
      .map(function (key) {
        return encodeURIComponent(key) + '=' + encodeURIComponent(data[key])
      })
      .join('&')

    url = url + ((url.indexOf('?') == -1 ? '?' : '&') + params)
  }

  let xhr,
    formData = null
  xhr = new XMLHttpRequest()
  xhr.withCredentials = false

  xhr.open('GET', url)
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
  xhr.onload = function () {
    if (xhr.status != 200) {
      return
    }

    try {
      let res = JSON.parse(xhr.responseText) || null
      if (!res) {
        return
      }
      callback(res, xhr)
    } catch (e) {
      return
    }
  }

  xhr.send(formData)
}

// 验证失败的回调
const captchaFail = () => {
  emit('captchaFail')
}
// 验证成功的回调
const captchaSuccess = (data) => {
  emit('captchaSuccess', data)
}

// 验证完成的回调
const captchaComplete = (status) => {
  emit('captchaComplete', status)
}
// 弹窗关闭的回调
const close = () => {
  emit('update:captchaShow', false)
  emit('close')
}
// 图形验证码相关的 end
</script>

<style lang="scss" scoped></style>

页面使用

<template>
   <routate-captcha v-model:captchaShow="captchaShow" @success="captchaSuccess" />
</template>
<script setup>
// 图形验证码相关的 start
const captchaShow = ref(false)
const captchaSuccess = (data) => {
   // 回调成功的逻辑
}
// 图形验证码相关的 end
</script>

效果地址

要看具体效果可以看这个地址cloud.35.com/user/regist…

以上是整个插件的配置及使用,有问题欢迎大家评论区或者私信留言哈