一, captcha
`captcha.js` 是一个生成验证码的插件, 使用 JS 和 canvas 生成的, 确保后端服务被暴力攻击, 简单判断人机以及系统的安全性, 体积小, 功能多, 支持配置.
验证码插件内容, 包含 1, 验证码插件 - 使用, 2, 验证码插件栗子, 3,API 介绍, 4, 支持浏览器
注意: 基于本项目源码从事科研, 论文, 系统开发,"最好" 在文中或系统中表明来自于本项目的内容和创意, 否则所有贡献者可能会鄙视你和你的项目. 使用本项目源码请尊重程序员职业和劳动
二, 功能
+ 版本 v 1.0.0
- 1, 支持随机字符内容配置, 字符大小配置, 字符类型配置, 字符绘制方式配置, 字符长度配置等
- 2, 支持点位置随机, 数量配置, 点半径的配置
- 3, 支持线条位置随机, 宽度配置, 线条数量的配置
- 4, 支持随机前景色配置, 区间值[0, 255], 可以使用默认值
- 5, 支持随机背景色配置, 区间值[0, 255], 可以使用默认值
- 6, 支持点击更新视图
- 7, 支持浏览器谷歌浏览器, 火狐浏览器, Safari,IE10 + 等
三, 验证码插件 - 使用
不依赖与其他的插件, 实现起来很容易,`captcha.js` 是必须要引进的组件
3.1 本地引入封装的 JS 文件
第一步: 获取组件方式:`git clone https://github.com/saucxs/captcha.git`
第二步: clone 后, 在需要加验证码的相关页面引入验证码文件 "captcha.js" 以及准备好装验证码容器:
引入 captcha 内容
<script type="text/javascript" src="./captcha.js"></script>
装验证码的容器
<canvas width="240" height="90" id="captcha1"></canvas>
第三步: 在确保页面 DOM 加载完毕之后, 调用 captcha 的 draw 方法(手动加载):
- /* 不传值, 统一走默认值 */
- let captcha1 = new Captcha();
- captcha1.draw(document.querySelector('#captcha1'), r => {
- console.log(r, '验证码 1');
- });
- /* 传值, 参数配置值, 选择性配置 */
- let captcha2 = new Captcha({
- lineWidth: 1, // 线条宽度
- lineNum: 6, // 线条数量
- dotR: 2, // 点的半径
- dotNum: 25, // 点的数量
- preGroundColor: [10, 80], // 前景色区间
- backGroundColor: [150, 250], // 背景色区间
- fontSize: 30, // 字体大小
- fontFamily: ['Georgia', '微软雅黑', 'Helvetica', 'Arial'], // 字体类型
- fontStyle: 'stroke', // 字体绘制方法, 有 fill 和 stroke
- content: '一个验证码 abcdefghijklmnopqrstuvw 生成的插件使用的是 canvas 显示', // 验证码内容
- length: 6 // 验证码长度
- });
- captcha2.draw(document.querySelector('#captcha2'), r => {
- console.log(r, '验证码 2');
- });
使用插件的效果地址 1:https://www.mwcxs.top/static/testTool/demo/index.html
3.2 NPM 包引入
第一步: NPM 获取验证码组件:
NPM install captcha-mini
第二步: 引入验证码模块:
import Captcha from 'captcha-mini'
或者
var Captcha = require("captcha-mini")
第三步: 在确保页面 DOM 加载完毕之后, 调用 captcha 的 draw 方法(手动加载):
- /* 不传值, 统一走默认值 */
- let captcha1 = new Captcha();
- captcha1.draw(document.querySelector('#captcha1'), r => {
- console.log(r, '验证码 1');
- });
- /* 传值, 参数配置值, 选择性配置 */
- let captcha2 = new Captcha({
- lineWidth: 1, // 线条宽度
- lineNum: 6, // 线条数量
- dotR: 2, // 点的半径
- dotNum: 25, // 点的数量
- preGroundColor: [10, 80], // 前景色区间
- backGroundColor: [150, 250], // 背景色区间
- fontSize: 30, // 字体大小
- fontFamily: ['Georgia', '微软雅黑', 'Helvetica', 'Arial'], // 字体类型
- fontStyle: 'stroke', // 字体绘制方法, 有 fill 和 stroke
- content: '一个验证码 abcdefghijklmnopqrstuvw 生成的插件使用的是 canvas 显示', // 验证码内容
- length: 6 // 验证码长度
- });
- captcha2.draw(document.querySelector('#captcha2'), r => {
- console.log(r, '验证码 2');
- });
四, 原理
1, 思路
现在我们需要一个对象, 然后调用对象的某个方法可以将验证码画出来. 所以我们需要一个构造函数, 用来实例化对象.
function Regcode() {}
构造函数接受一些参数, 用来定制验证码的点, 线, 字的各种属性(颜色, 长短, 大小等).
- function Regcode(params = {}) {
- let p = Object.assign({...}, params); // 这里有定义好的属性和默认值
- Object.keys(p).forEach(k => { // 将所有属性组合后添加到 this 上
- this[k] = p[k];
- });
- }
2,draw 方法
首先我们需要一个 draw 方法, 作为验证码的绘制方法.
draw 方法接收两个参数, canvas 的 dom 对象, 用来创建绘图的 2d 对象. 还需要一个回调函数 callback, 用来接收每次绘制的文字.
我们把 draw 方法放在 Regcode 的原型上, 这样所有的实例对象都可以继承这些方法, 而不是自己独立有一套.
在 draw 方法中, 可以想到的是, 我们需要创建 canvas 的 2d 对象, 创建画布, 然后开始依次绘制点, 线, 文字.
- Regcode.prototype.draw = function(dom, callback = function () {}) { // 绘图
- // 获取 canvas dom
- if (!this.paint) { // 如果没有 2d 对象, 再进行赋值操作
- this.canvas = dom; // 保存到 this 指针, 方便使用
- if (!this.canvas) return;
- this.paint = this.canvas.getContext('2d'); // 保存到 this 指针, 方便使用
- if (!this.paint) return;
- // 回调函数赋值给 this, 方便使用
- this.callback = callback;
- }
- // 随机画布颜色, 使用背景色
- 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.arc();
- this.line();
- this.font();
- };
需要简单判断一下是否有 dom 对象和 2d 对象, 其实应该判断参数是否为 dom 对象, 可以通过判断节点类型或者通过 dom instanceof HTMLElement(谷歌和火狐支持)来判断. 但是这里因为要求不高, 所以只是简单判断.
3, 随机颜色
需要注意的是, 在创建画布的时候, 我们使用了获取背景色的一个方法. 在之前的需求中我们可以看到, 最高频的两个词是随机和颜色, 所以肯定是需要将这两个方法单独封装的.
随机颜色这里采用的是 rgb 的强度值 (0 ~ 255, 由暗 -> 亮), 需要指定两个颜色区间: 前景色(文字, 线条) 和背景色(画布背景). 因为需要将文字和背景颜色区分, 避免色值太接近无法识别, 所以默认前景色区间 [10, 80], 背景色区间 [150, 250].
- Regcode.prototype.getColor = function(arr) { // 随机获取颜色
- let colors = new Array(3).fill(''); // 创建一个长度为 3 的数组, 值都填充为''
- colors = colors.map(v => this.getRand(...arr)); // 每个成员随机获取一个强度值重组为新数组
- return colors;
- };
因为 rgb 颜色通常表示为 rgba(0,0,0,0.8), 最后一位是透明度, 这里没有参加随机. 所以只考虑前 3 个数, 在指定的强度区间内, 只需要依次随机出 3 个数就好. 所以在上面的方法中, 还需要做的就是随机在一个数值区间中取值.
- Regcode.prototype.getRand = function(...arr) { // 获取某个区间的随机数
- arr.sort((a, b) => a - b); // 将传入的参数从小到大排序
- return Math.floor(Math.random() * (arr[1] - arr[0]) + arr[0]);
- };
4, 绘制线条
有了随机颜色, 绘制线条就方便多了. lineNum 用于指定绘制几条线, 默认为 2 条. 之前说过前景色 (foregroundColor) 和 背景色 (backgroundColor) 也是可以传参的, 文字, 线条, 点都使用前景色. 在绘制线条的时候, 还需要计算出线条的随机起止坐标, 在这里 canvas 的宽高范围内都允许, 这样就可以做到随机长度.
- Regcode.prototype.line = function() { // 绘制线条
- for (let i = 0; i < this.lineNum; i++) {
- // 随机获取线条的起止坐标
- let x = this.getRand(0, this.canvas.width), y = this.getRand(0, this.canvas.height),
- endx = this.getRand(0, this.canvas.width), endy = this.getRand(0, this.canvas.width);
- this.paint.beginPath(); // 开始绘制
- this.paint.lineWidth = this.lineWidth;
- // 随机获取路径颜色
- let colors = this.getColor(this.foregroundColor); // 使用前景色
- 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(); // 进行绘制
- }
- };
5, 绘制圆点
绘制圆点要注意的是需要随机获取圆心的位置, 即分别随机获取在宽高范围内的 (x, y) 坐标. dotNum 是允许传入的需要绘制圆点的个数, 默认为 10,dotR 是半径, 默认为 1.
- Regcode.prototype.arc = function() { // 绘制圆点
- for (let i = 0; i < this.dotNum; i++) {
- // 随机获取圆心
- let x = this.getRand(0, this.canvas.width), y = this.getRand(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.foregroundColor);
- this.paint.fillStyle = `rgba(${colors[0]}, ${colors[1]}, ${colors[2]}, 0.8)`;
- // 绘制
- this.paint.fill();
- }
- };
6, 绘制文字
绘制文字稍微麻烦一些, 需要先从定义好的验证码因子(允许通过 content 参数自定义, 默认为
acdefhijkmnpwxyABCDEFGHJKMNPQWXY12345789
, 这里去掉了类似于字母 b 和 数字 6 这样的容易混淆的字符.)中, 随机获取指定长度 (允许通过参数自定义) 的验证码.
- Regcode.prototype.getText = function() { // 随机获取验证码
- let len = this.content.length, str = '';
- for (let i = 0; i < this.len; i++) { // 随机获取每个因子, 组成验证码
- str += this.content[this.getRand(0, len)];
- }
- return str;
- };
绘制文字的时候, 注意以下几点:
1, 需要通过回调函数将当前绘制的文字输出.
2, 需要指定文字的旋转角度, 字体类型, 文字颜色, 绘制风格(填充或者不填充).
3, 需要获得文字的实际宽度, 用来确定单个文字的活动范围.
- Regcode.prototype.font = function() { // 绘制文字
- let str = this.getText(); // 获取验证码
- this.callback(str); // 利用回调函数输出文字, 用于与用户输入验证码进行比对
- // 指定文字风格
- this.paint.font = `${this.fontSize}px ${this.fontFamily}`;
- this.paint.textBaseline = 'middle'; // 设置文本基线, middle 是整个文字所占方框的高度的正中.
- // 指定文字绘制风格
- let fontStyle = `${this.fontStyle}Text`;
- let colorStyle = `${this.fontStyle}Style`;
- for (let i = 0; i < this.len; i++) { // 循环绘制每个字
- let fw = this.paint.measureText(str[i]).width; // 获取文字绘制的实际宽度
- // 获取每个字的允许范围, 用来确定绘制单个文字的横坐标
- let x = this.getRand(this.canvas.width / this.len * i, (this.canvas.width / this.len) * i + fw/2);
- // 随机获取字体的旋转角度
- let deg = this.getRand(-6, 6);
- // 随机获取文字颜色
- let colors = this.getColor(this.foregroundColor);
- 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();
- }
- };
基本上就完成了.
五, 其他
欢迎使用 [watermark-dom](https://github.com/saucxs/watermark-dom) 插件, 功能: 给 B/S 网站系统加一个很浅的 dom 水印插件.
欢迎使用 [captcha-mini]( https://github.com/saucxs/captcha ) 插件, 功能: 生成验证码的插件, 使用 JS 和 canvas 生成的
欢迎使用 [watermark-image]( https://github.com/saucxs/watermark-image ) 插件, 目前功能: 图片打马赛克
来源: https://www.cnblogs.com/chengxs/p/10480504.html