前端验证码的绘制

效果图如下:
步骤:

1.安装jsencrypt,创建jsencrypt.js文件对从后端获取的验证码进行解密

javascript 复制代码
import JSEncrypt from 'jsencrypt'
import { parseBigInt } from 'jsencrypt/lib/lib/jsbn/jsbn'
//私钥
let privateKey = `xxxxxx...` ('BEGIN RSA PRIVATE KEY-----\n${加密的文本}\n-----END RSA PRIVATE KEY')
const publicKey ='xxxxxx...' ('BEGIN RSA PUBLIC KEY-----\n${加密的文本}\n-----END RSA PUBLIC KEY')
const encryptor = new JSEncrypt()

/**
 * 公钥--RSA加密
 * @param {*} word 需要加密的字符串
 * @param {*} publicKey 公钥
 * @returns
 */
export function rsa_encrypt(word) {
  encryptor.setPublicKey(publicKey)
  return encryptor.encrypt(word)
}

/**
 * RSA解密(私钥解密)
 * @param {*} word 需要解密的字符串
 * @param {*} privateKey 私钥
 * @returns
 */
export function rsa_decrypt(word, privateKey) {
  // privateKey = `-----BEGIN RSA PRIVATE KEY-----\n${word}\n-----END RSA PRIVATE KEY-----`
  encryptor.setPrivateKey(privateKey)
  return encryptor.decrypt(word)
}

/**
 * RSA解密(公钥解密)
 * @param {*} data 需要解密的字符串
 * @param {*} publicKey 公钥
 * @returns
 */
export function decrypt(data) {
  const encrypt = new JSEncrypt()
  encrypt.setPublicKey(publicKey)
  // 不支持公钥解密
  // 自定义解析方法支持公钥解析
  const rsaKey = encrypt.getKey()
  rsaKey.decrypt = function (ctext) {
    let c = parseBigInt(ctext, 16)
    let m = this.doPublic(c)
    if (m == null) {
      return null
    }
    return pkcs1unpad2(m, (this.n.bitLength() + 7) >> 3)
  }
  return encrypt.decrypt(data)
}

export function pkcs1unpad2(d, n) {
  let b = d.toByteArray()
  let i = 0
  while (i < b.length && b[i] === 0) {
    ++i
  }
  ++i
  while (b[i] !== 0) {
    if (++i >= b.length) {
      return null
    }
  }
  let ret = ''
  while (++i < b.length) {
    let c = b[i] & 255
    if (c < 128) {
      // utf-8 decode
      ret += String.fromCharCode(c)
    } else if (c > 191 && c < 224) {
      ret += String.fromCharCode(((c & 31) << 6) | (b[i + 1] & 63))
      ++i
    } else {
      ret += String.fromCharCode(((c & 15) << 12) | ((b[i + 1] & 63) << 6) | (b[i + 2] & 63))
      i += 2
    }
  }
  return ret
}

2.创建verfiycode.js文件,绘制验证码画布的方法

kotlin 复制代码
import { rsa_decrypt } from './jsencrypt.js'
const privateKey = 'xxxxxx...'   ('BEGIN RSA PRIVATE KEY-----\n${加密的文本}\n-----END RSA PRIVATE KEY')
function CaptchaMini(params = {}) {
  let middleParams = Object.assign(
    {
      lineWidth: 0.5, //线条宽度
      lineNum: 2, //线条数量
      dotR: 1, //点的半径
      dotNum: 15, //点的数量
      preGroundColor: [10, 80], //前景色区间
      backGroundColor: [150, 250], //背景色区间
      fontSize: 20, //字体大小
      fontFamily: ['Georgia', '微软雅黑', 'Helvetica', 'Arial'], //字体类型
      fontStyle: 'fill', //字体绘制方法,有fill和stroke
      content: '1234', //验证码内容
      length: 4 //验证码长度
    },
    params
  )
  Object.keys(middleParams).forEach(item => {
    this[item] = middleParams[item]
  })
  this.canvas = null
  this.paint = null
  this.oricode = null
}

/*Captcha的原型上绑定方法
 * 获取区间的随机数
 * params []*/
CaptchaMini.prototype.getRandom = function (...arr) {
  arr.sort((a, b) => a - b)
  return Math.floor(Math.random() * (arr[1] - arr[0]) + arr[0])
}

/*Captcha的原型上绑定方法
 * 获取随机颜色
 * params []*/
CaptchaMini.prototype.getColor = function (arr) {
  let colors = new Array(3).fill('')
  colors = colors.map(() => this.getRandom(...arr))
  return colors
}

/*Captcha的原型上绑定方法
 * 获取验证码*/
CaptchaMini.prototype.getText = function () {
  debugger
  let length = this.content.length
  let str = ''
  for (let i = 0; i < this.length; i++) {
    str += this.content[this.getRandom(0, length)]
  }
  return str
}

/*Captcha的原型上绑定方法
 * 绘制线条*/
CaptchaMini.prototype.line = function () {
  for (let i = 0; i < this.lineNum; i++) {
    /*随机获取线条的起始位置*/
    let x = this.getRandom(0, this.canvas.width)
    let y = this.getRandom(0, this.canvas.height)
    let endX = this.getRandom(0, this.canvas.width)
    let endY = this.getRandom(0, this.canvas.height)

    this.paint.beginPath()
    this.paint.lineWidth = this.lineWidth

    /*获取颜色路径*/
    let colors = this.getColor(this.preGroundColor)
    this.paint.strokeStyle = 'rgba(' + colors[0] + ',' + colors[1] + ',' + colors[2] + ',' + '0.8)'

    /*绘制路径*/
    this.paint.moveTo(x, y)
    this.paint.lineTo(endX, endY)
    this.paint.closePath()
    this.paint.stroke()
  }
}

/*Captcha的原型上绑定方法
 * 绘制圆点*/
CaptchaMini.prototype.circle = function () {
  for (let i = 0; i < this.dotNum; i++) {
    /*随机获取圆心*/
    let x = this.getRandom(0, this.canvas.width)
    let y = this.getRandom(0, this.canvas.height)
    this.paint.beginPath()
    /*绘制圆*/
    this.paint.arc(x, y, this.dotR, 0, Math.PI * 2, false)
    this.paint.closePath()
    /*随机获取路径颜色*/
    let colors = this.getColor(this.preGroundColor)
    this.paint.fillStyle = 'rgba(' + colors[0] + ',' + colors[1] + ',' + colors[2] + ',' + '0.8)'
    /*绘制*/
    this.paint.fill()
  }
}

/*Captcha的原型上绑定方法
 * 绘制文字*/
CaptchaMini.prototype.font = function () {
  let str = rsa_decrypt(this.content, privateKey) // 解密验证码
  console.log(str, 'str')
  this.oricode = str
  this.callback(str)
  /*指定文字风格*/
  this.paint.font = this.fontSize + 'px ' + this.fontFamily[this.getRandom(0, this.fontFamily.length)]
  this.paint.textBaseline = 'middle'
  /*指定文字绘制风格*/
  let fontStyle = this.fontStyle + 'Text'
  let colorStyle = this.fontStyle + 'Style'
  for (let i = 0; i < this.length; i++) {
    let fontWidth = this.paint.measureText(str[i]).width
    let x = this.getRandom(
      (this.canvas.width / this.length) * i + 0.2 * fontWidth,
      (this.canvas.width / this.length) * i + 0.5 * fontWidth
    )
    /*随机获取字体的旋转角度*/
    let deg = this.getRandom(-6, 6)
    /*随机获取文字颜色*/
    let colors = this.getColor(this.preGroundColor)
    this.paint[colorStyle] = 'rgba(' + colors[0] + ',' + colors[1] + ',' + colors[2] + ',' + '0.8)'
    /*开始绘制*/
    this.paint.save()
    this.paint.rotate((deg * Math.PI) / 180)
    this.paint[fontStyle](str[i], x, this.canvas.height / 2)
    this.paint.restore()
  }
}

/*Captcha的原型上绑定方法
 * 绘制图形*/
CaptchaMini.prototype.draw = function (dom, callback = function () {}) {
  /*获取canvas的dom*/
  if (!this.paint) {
    this.canvas = dom
    if (!this.canvas) return
    else this.paint = this.canvas.getContext('2d')
    /*回调函数赋值给this*/
    this.callback = callback
    this.canvas.onclick = () => {
      this.drawAgain()
    }
  }
  /*随机画布颜色,使用背景色*/
  let colors = this.getColor(this.backGroundColor)
  this.paint.fillStyle = 'rgba(' + colors[0] + ',' + colors[1] + ',' + colors[2] + ',' + '0.8)'

  /*绘制画布*/
  this.paint.fillRect(0, 0, this.canvas.width, this.canvas.height)

  /*绘图*/
  this.circle()
  this.line()
  this.font()
}

/*Captcha的原型上绑定方法
 * 清空画布*/
CaptchaMini.prototype.clear = function () {
  this.paint.clearRect(0, 0, this.canvas.width, this.canvas.height)
}

/*Captcha的原型上绑定方法
 * 重新绘制*/
CaptchaMini.prototype.drawAgain = function () {
  this.clear()
  this.draw(this.callbak)
}

export default CaptchaMini

3.准备canvas容器,进行绘制

typescript 复制代码
// html
<canvas id="captcha1" width="80" height="32" style="margin-left: 10px" @click="changeVerfiyCode"></canvas>



// js
import Captcha from '/verfiycode.js'
const changeVerfiyCode = () => {
// 获取从后端发返回的加密的验证码
  getCode().then(res => {
    console.log('res', res)
    genCaptcha(res.encryptVerifyCode)
    // 登录信息
   // logInfo.oriCode = captcha1.oriCode
   // logInfo.verifyCodeUuid = res.verifyCodeUuid
  })
}
const genCaptcha = str => {
  captcha1 = new Captcha({
    content: str
  })
  // 绘制验证码
  captcha1.draw(document.querySelector('#captcha1'), r => {
    console.log(r, '验证码1')
  })
}
相关推荐
zhangxingchao1 分钟前
Android开发者如何快速上手Flutter开发
前端
空&白16 分钟前
css元素的after制作斜向的删除线
前端·css
海盐泡泡龟16 分钟前
“组件、路由懒加载”,在 Vue3 和 React 中分别如何实现? (copy)
前端·javascript·react.js
_揽1 小时前
html如何在一张图片上的某一个区域做到点击事件
前端·html
踢足球的,程序猿1 小时前
从 Vue 2.0 进阶到 Vue 3.0 的核心技术解析指南
前端·javascript·vue.js·前端框架·html
冷凌爱1 小时前
Fetch与Axios:区别、联系、优缺点及使用差异
前端·node.js·js
袁煦丞1 小时前
跨平台终端王者Tabby:cpolar内网穿透实验室第632个成功挑战
前端·程序员·远程工作
Sailing1 小时前
Grafana-mcp-analyzer:基于 MCP 的轻量 AI 分析监控图表的运维神器!
前端·node.js·mcp
阿山同学.2 小时前
AWS 亚马逊 S3存储桶直传 前端demo 复制即可使用
前端·javascript·aws
Jolyne_2 小时前
grid 实现完美的水平铺满、间隔一致的自适应布局
前端·css