生成验证码
构建简单的node服务器
- 新建serve文件夹
- 进入 serve文件夹初始化npm项目 npm init
- 新建server.js文件
const express = require('express');
const http = require('http');
const app = express();
const server = http.createServer(app);
const svgCaptcha = require('svg-captcha'); //验证码插件
//设置允许跨域访问
app.all("*", function(req, res, next) {
//设置允许跨域的域名,*代表允许任意域名跨域
res.header("Access-Control-Allow-Origin", "*");
//允许的header类型
res.header("Access-Control-Allow-Headers", "content-type");
//跨域允许的请求方式
res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == 'options')
res.send(200); //让options尝试请求快速结束
else
next();
})
// 获取验证码接口
app.get('/getCodeImg', (req, res) => {
// 字母验数字证码
// let codeConfig = {
// size: 4,// 验证码长度
// ignoreChars: '0o1i', // 验证码字符中排除 0o1i
// noise: 5, // 干扰线条的数量
// width: 100, //验证码宽度
// height: 40, //验证码高度
// fontSize: 40, //字体大小
// color: true, //开启字体颜色
// background: '#cc9966',//背景颜色
// // charPreset:'abcd123456789', //随机预设字符
// }
// let captcha = svgCaptcha.create(codeConfig); //验证码
// 数学表算式验证码
let codeConfig = {
mathMin: 1, //数学表达式的最小值
mathMax: 20, // 数学表达式的最大值
mathOperator: '+', // 使用的运算符:+、-或+-(用于随机的+或-)
noise: 5, // 干扰线条的数量
width: 100, //验证码宽度
height: 40, //验证码高度
fontSize: 40, //字体大小
color: true, //开启字体颜色
background: '#cc9966',//背景颜色
}
let captcha = svgCaptcha.createMathExpr(codeConfig); //算式验证码
//captcha.text 验证码的文字码
//captcha.data 验证码图片的svg数据
res.json({
status: 200,
data: captcha.data
})
});
server.listen(3000, () => {
console.log("server is up and running on http://127.0.0.1:3000");
});
- 安装express包
npm i express -s - 安装svg-captcha包
npm i svg-captcha -s - 启动服务器
node server.js
在serve文件同级目录新建客户端网页 client.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>验证码</title>
</head>
<style>
.main {
width: 1000px;
margin: 0 auto;
padding-top: 100px;
}
.code {
height: 40px;
line-height: 40px;
padding: 0 4px;
box-sizing: border-box;
outline: 0;
font-size: 16px;
}
.code-img {
vertical-align: middle;
margin-left: 20px;
}
.btn {
cursor: pointer;
color: #fff;
background-color: #409eff;
border: 1px solid #409eff;
text-align: center;
box-sizing: border-box;
outline: 0;
margin: 0;
font-weight: 500;
padding: 12px 20px;
font-size: 14px;
border-radius: 4px;
}
</style>
<body>
<div class="main">
<div>
<input type="text" class="code" id="code">
<sapn class="code-img" id="codeImg" onclick="getCodeImg()"></sapn>
</div>
<button class="btn" >验证</button>
</div>
</body>
</html>
<script>
window.onload = function() {
getCodeImg();
}
async function getCodeImg() {
// 请求接口获取验证码数据
// fetch 默认是get请求
let response = await fetch('http://127.0.0.1:3000/getCodeImg');
response.json().then(res => {
document.querySelector('#codeImg').innerHTML = res.data; //渲染验证码图片
})
}
</script>
这时候运行网页就可以看到验证码
验证
在验证之前我们要先将验证码的答案保存起来,为了确保验证码答案不会被另一个浏览器的验证码覆盖,使用fingerprint2插件获取浏览器的指纹ID
修改客户端网页代码
- 引用 fingerprint2
<script src="https://lib.baomitu.com/fingerprintjs2/2.1.5/fingerprint2.js"></script>
- 获取浏览器的指纹ID
// 获取浏览器指纹
function getFingerprint() {
// 因为是异步的所以使用 Promise
return new Promise((resolve, reject) => {
// 浏览器指纹
Fingerprint2.get((components) => {
const values = components.map(component => component.value);
const murmur = Fingerprint2.x64hash128(values.join(''), 31); // 生成浏览器指纹
resolve(murmur)
});
})
}
- 将指纹ID作为获取获取验证码接口的参数
let browserId = ''; //定义浏览器id全局变量
window.onload = function() {
getFingerprint().then(res => {
browserId = res;
getCodeImg();
})
}
async function getCodeImg() {
// 请求接口获取验证码数据
// fetch 默认是get请求
let response = await fetch('http://127.0.0.1:3000/getCodeImg?browserId=' + browserId);
response.json().then(res => {
document.querySelector('#codeImg').innerHTML = res.data; //渲染验证码图片
})
}
服务端代码修改
- 因为没有使用数据库,所以我们将数据保存在本地json文件
- 在server.js同级目录创建json文件 db.json
const fs = require('fs'); //引入 fs 核心模块
const dbJson = './db.json'; //文件路径
- 在getCodeImg接口添加代码
let browserId = req.query.browserId; // 获取浏览器指纹id参数
fs.readFile(dbJson, function(err, data) {
if (err) {
return console.error(err);
}
//将二进制的数据转换为字符串
let str = data.toString();
let verificationCode = [];
//将字符串转换为json对象
if (str) {
verificationCode = JSON.parse(str);
}
let index = verificationCode.findIndex(item => item.browserId === browserId)
console.log(str, 'str')
console.log(index, 'index')
if (index > -1) {
verificationCode[index].answer = captcha.text
} else {
let obj = {
browserId: browserId,
answer: captcha.text
}
verificationCode.push(obj);//将传来的对象push进数组对象中
}
let dataStr = JSON.stringify(verificationCode);//把json对象转换成字符串重新写入json文件中
fs.writeFile(dbJson, dataStr, function(err) {
if (err) {
console.error(err);
}
})
})
- 编写验证接口
app.post('/validationCode', (req, res) => {
var data = '';
req.on('data', function(chunk) {
data += chunk;
});
req.on('end', function() {
data = decodeURI(data);
var dataObject = JSON.parse(data);
fs.readFile(dbJson, function(err, data) {
if (err) {
return console.error(err);
}
let verificationCode = JSON.parse(data.toString()) || [];
let captcha = verificationCode.filter(item => item.browserId === dataObject.browserId)[0]
if (dataObject.code === captcha.answer) {
res.json({
status: 200,
msg: '验证码正确',
})
} else {
res.json({
status: 201,
msg: '验证码错误'
})
}
})
});
});
修改ndoe代码要重启服务才会生效
客户端验证按钮
// 验证
document.querySelector('#verificationBtn').addEventListener('click', async function() {
let code = document.querySelector('#code').value
let param = {
browserId: browserId,
code: code
}
let response = await fetch('http://127.0.0.1:3000/validationCode', {
method: "POST",
body: JSON.stringify(param)
})
response.json().then(res => {
alert(res.msg)
})
})
最终效果如下
源码地址
https://gitee.com/swfcode/code-img.git