前端验证码的绘制

效果图如下:
步骤:

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')
  })
}
相关推荐
qiyi.sky7 分钟前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~10 分钟前
分析JS Crash(进程崩溃)
java·前端·javascript
安冬的码畜日常19 分钟前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺
l1x1n01 小时前
No.3 笔记 | Web安全基础:Web1.0 - 3.0 发展史
前端·http·html
昨天;明天。今天。1 小时前
案例-任务清单
前端·javascript·css
zqx_72 小时前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己3 小时前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称3 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色3 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_2343 小时前
npm、yarn、pnpm之间的区别
前端·npm·node.js