目前咱們所能接收到的驗證碼有前端
短信/郵件 - 使用數/字符串隨機組合,通常長度爲4~6node
識別圖片 - 字符串/運算跨域
滑塊 - 拼圖緩存
點選 - 點擊目標文字/圖片,順序點擊等服務器
接下來咱們使用Koa實現上述的集中驗證碼,在此以前咱們須要安裝一些插件app
圖片識別類的驗證碼有不少,好比cors
基礎功能不盡相同,能夠自行嘗試。dom
因爲本文須要與前端進行交互,還須要koa
來解決跨域。async
在開始寫代碼以前 咱們固然要解決跨域的問題,在app.js中寫入配置
const cors = require('koa2-cors') app.use(cors({ origin: function (ctx) { return '*'; }, exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'], maxAge: 5, credentials: true, allowMethods: ['GET', 'POST', 'DELETE'], allowHeaders: ['Content-Type', 'Authorization', 'Accept'], }))
隨機字符,很簡單,咱們使用svg-captcha來隨機生成字符及圖片,將圖片返回前端,服務端存取其對應值來進行驗證便可。
直接上代碼
const svgCaptcha = require('svg-captcha') const getString = async () => { const cap = svgCaptcha.create({ size: 4, // 驗證碼長度 width:160, height:60, fontSize: 50, ignoreChars: '0oO1ilI', // 驗證碼字符中排除 0o1i noise: 2, // 干擾線條的數量 color: true, // 驗證碼的字符是否有顏色,默認沒有,若是設定了背景,則默認有 background: '#eee' // 驗證碼圖片背景顏色 }) let img = cap.data // 驗證碼 var text = cap.text.toLowerCase() // 驗證碼字符,忽略大小寫 return {svg: `${img}<span >${text}</span>`} }
咱們會返回給客戶端"svg",直接渲染便可。
一樣咱們也使用svg-captcha來實現
const svgCaptcha = require('svg-captcha') const getNumber = async () => { const cap = svgCaptcha.createMathExpr({ size: 4, // 驗證碼長度 width:160, height:60, fontSize: 50, ignoreChars: '0oO1ilI', // 驗證碼字符中排除 0o1i noise: 2, // 干擾線條的數量 color: true, // 驗證碼的字符是否有顏色,默認沒有,若是設定了背景,則默認有 background: '#eee' // 驗證碼圖片背景顏色 }) let img = cap.data // 驗證碼 var text = cap.text.toLowerCase() // 驗證碼字符,忽略大小寫 return {svg: `${img}<span>${text}</span>`} }
同上。
滑動模塊實現較爲複雜,但其邏輯仍是很簡單,咱們先來梳理一下實現邏輯。
服務端:
服務端生成上述信息返回客戶端(座標值返回y)。
客戶端:
const gm = require('gm').subClass({imageMagick: true}); var arrBuffer = [] const getSlide = async () => { arrBuffer = [] const width = 420 const height = 250 const fragmentSize = 50 try { // 生成圖片 const filePath = getRandomPath() const x = (Math.floor(Math.random() * 1000) % (width - 2 * fragmentSize)) + fragmentSize const y = Math.floor(Math.random() * 1000) % (height - fragmentSize) const { image, fragment } = await createImage(filePath, width, height, fragmentSize, x, y) // 緩存記錄 arrBuffer.push({x}) console.log(arrBuffer) return { msg: "ok", data: { image, fragment, y } } } catch (err) { return { msg: "服務器錯誤:" + err, data: null } } } function getRandomPath() { const fileLength = 4 const index = Math.floor(Math.random() * 1000) % fileLength return path.resolve(__dirname, `../static/images/${index + 1}.jpg`) } function createImage(filePath, w, h, s, x, y) { return new Promise((resolve, reject) => { const res = { image: "", fragment: "" } gm(filePath) .resize(w, h, "!") .fill("rgba(0,0,0,.5)") //繪製由座標對、寬度和高度指定的矩形。 .drawRectangle(x, y, x + s - 1, y + s - 1) .noProfile() .setFormat('jpeg') .toBuffer( (err, buffer) => { if (err) { reject(err) } res.image = "data:image/jpg;base64," + buffer.toString("base64") gm(filePath) .resize(w, h, "!") .crop(s, s, x, y) .noProfile() .setFormat('jpeg') .toBuffer((err, buffer) => { if (err) { reject(err) } res.fragment = "data:image/jpg;base64," + buffer.toString("base64") resolve(res) }) }) }) }
這樣咱們就能夠拿到,兩張圖片及其座標。
大圖
小圖
前端根據兩張圖及y座標就能夠實現滑塊的初始化
滑動的過程相信你們都會寫!在這不盡行詳細描述。當咱們監聽到滑動結束後,將此時小圖的 clienX值記錄返回服務端,與服務端緩存的X座標進行匹配。
那麼咱們來實現Check接口
const check = async (data) => { const {x} = data const isMatch = Math.abs(x - arrBuffer[0].x) < 5 if (isMatch) { return {success: true, msg: '驗證成功,已超過99.9%的用戶'} } else { return {success: false, msg: '驗證失敗'} } }
郵件驗證碼難點在於發送郵件。咱們只須要將隨機生成的字符串經過郵件發送至目標客戶端便可,短信也是一樣的道理。
咱們來實現一個簡單的郵件發送接口。
const nodemailer = require('nodemailer') const smtpConfig = { host: 'smtp.163.com', port: 465, secure: true, auth: { user: '', pass: '' } } const mail = async (data) => { var transporter = nodemailer.createTransport(smtpConfig); let mailOptions = { from: '', to: '', subject: '驗證碼', text: Math.random().toString(36).substr(2,4) } transporter.sendMail(mailOptions, (error, info) => { if(error){ return console.log(error); } console.log(info) }) return {success: true} }