Springboot3.x 中實(shí)現(xiàn)滑動(dòng)拼圖驗(yàn)證碼的全面指南
滑動(dòng)拼圖驗(yàn)證碼作為用戶身份驗(yàn)證的一種方式,它的主要作用是進(jìn)行人機(jī)行為區(qū)分,防止針對(duì)網(wǎng)站或者應(yīng)用的惡意機(jī)器行為。這種驗(yàn)證碼尤其適用于防止惡意的批量注冊(cè)、登錄爬蟲以及自動(dòng)化攻擊,比如說防止一些驗(yàn)證碼識(shí)別的機(jī)器人。
基本的滑動(dòng)拼圖驗(yàn)證碼通常由一個(gè)背景圖像和一個(gè)缺失的圖像片段組成,用戶需要通過鼠標(biāo)操作將缺失的片段準(zhǔn)確滑動(dòng)至應(yīng)在的位置。在這個(gè)過程中,用戶的行為數(shù)據(jù)(包括滑動(dòng)軌跡,滑動(dòng)速度,滑動(dòng)時(shí)間等)都可以被用來進(jìn)行用戶和機(jī)器的區(qū)分,因此滑動(dòng)拼圖驗(yàn)證碼又有著非常高的安全性。
與傳統(tǒng)的文本驗(yàn)證碼相比,滑動(dòng)拼圖驗(yàn)證碼具有更好的用戶體驗(yàn)和更高的安全性:文本驗(yàn)證碼往往需要用戶仔細(xì)觀察,輸入錯(cuò)誤率高,給用戶帶來不便,而且現(xiàn)在有許多OCR技術(shù)可以成功識(shí)別文本驗(yàn)證碼?;瑒?dòng)拼圖驗(yàn)證碼則只需要用戶執(zhí)行一個(gè)自然的,直覺的動(dòng)作,用戶錯(cuò)誤率低,同時(shí)機(jī)器人模擬出人類完全一致的行為幾乎是不可能的。
理解原理:滑動(dòng)拼圖驗(yàn)證碼的運(yùn)行機(jī)制
滑動(dòng)拼圖驗(yàn)證碼的運(yùn)行包括了后端服務(wù)和前端交互兩大部分,我們先來知道整體的流程,再逐一深入。
- 后端生成圖像:后端首先生成一張完整的圖像和一個(gè)帶有缺口的復(fù)制圖像。這個(gè)缺口通常是一個(gè)特定的形狀,如圓形、方形等,或者是一段特定輪廓的形狀。這樣就形成了一對(duì)背景圖像和滑塊圖像,缺口的形狀和大小在這兩張圖像中是完全一致的。
- 前端展示與交互:后端將這兩張圖像發(fā)送給前端,前端將背景圖像正常展示,而滑塊圖像則位于一邊,等待用戶進(jìn)行拖動(dòng)。
- 用戶操作滑塊:用戶通過鼠標(biāo)或觸屏操作,將滑塊圖像拖動(dòng)至背景圖像中的缺口部分。在這個(gè)操作過程中,前端會(huì)記錄下用戶的行為數(shù)據(jù),這些數(shù)據(jù)包括但不限于滑動(dòng)開始和結(jié)束的時(shí)間、滑動(dòng)軌跡、滑動(dòng)速度等。
- 前端發(fā)送驗(yàn)證數(shù)據(jù):當(dāng)用戶完成拖動(dòng)后,或者用戶拖動(dòng)滑塊超出一定范圍后,前端將滑動(dòng)的結(jié)果以及行為數(shù)據(jù)發(fā)送給后端。這個(gè)結(jié)果就是滑塊在背景圖像中的位置,或者說是滑動(dòng)的距離。
- 后端驗(yàn)證:后端根據(jù)發(fā)送來的滑動(dòng)結(jié)果,以及用戶的行為數(shù)據(jù),進(jìn)行驗(yàn)證。如果滑動(dòng)結(jié)果與后端最初設(shè)定的缺口位置一致,或者在一定的誤差范圍內(nèi),同時(shí)用戶的行為數(shù)據(jù)也符合正常人類的行為,那么后端判定這次驗(yàn)證碼驗(yàn)證成功,返回驗(yàn)證成功的結(jié)果。
技術(shù)實(shí)現(xiàn):在Springboot3.x中如何生成滑動(dòng)拼圖驗(yàn)證碼
下面我們將實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 Spring Boot 工程來生成滑動(dòng)拼圖驗(yàn)證碼。我們的項(xiàng)目中主要包括四個(gè)部分,分別為原始圖片的獲取,將原始圖片轉(zhuǎn)換為帶缺口的滑動(dòng)驗(yàn)證碼圖片,前端的驗(yàn)證碼顯示和操作以及后端的數(shù)據(jù)驗(yàn)證。
后端:生成滑動(dòng)拼圖驗(yàn)證碼
我們首先需要一個(gè)原始圖片,這個(gè)圖片將會(huì)被轉(zhuǎn)換為驗(yàn)證碼圖片。原始圖片可以是任何圖片文件,例如originalImage.png,存放在項(xiàng)目的資源文件夾中。
后端用來生成驗(yàn)證碼的核心代碼如下:
@RestController
public class ImageController {
private final ImageService imageService;
public ImageController(ImageService imageService) {
this.imageService = imageService;
}
@GetMapping("/captcha")
public ResponseEntity<byte[]> getCaptcha(HttpSession session) throws IOException {
BufferedImage captchaImage = imageService.createCaptchaImage();
session.setAttribute("captcha", imageService.getCaptchaPosition());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(captchaImage, "png", baos);
byte[] imageInByte = baos.toByteArray();
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_TYPE, MediaType.IMAGE_PNG_VALUE)
.body(imageInByte);
}
@PostMapping("/validate")
public boolean validateCaptcha(@RequestParam int position, HttpSession session) {
Integer captchaPosition = (Integer) session.getAttribute("captcha");
if (captchaPosition != null) {
return Math.abs(captchaPosition - position) <= 5;
}
return false;
}
}
在這段代碼中,/captcha路由用來生成驗(yàn)證碼圖片并返回給前端,生成的驗(yàn)證碼圖片是一個(gè)PNG格式的圖片。同時(shí),將驗(yàn)證碼圖片中缺口的位置保存在session中,用來進(jìn)行后續(xù)的驗(yàn)證。/validate路由用來驗(yàn)證用戶的操作是否正確。
ImageService 類
import org.springframework.stereotype.Service;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import javax.imageio.ImageIO;
import java.util.Random;
@Service
public class ImageService {
private int captchaPosition;
public BufferedImage createCaptchaImage() throws IOException {
BufferedImage originalImage = readImageFromResources();
captchaPosition = generateRandomCaptchaPosition(originalImage.getWidth());
BufferedImage captchaImage = new BufferedImage(
originalImage.getWidth(),
originalImage.getHeight(),
originalImage.getType());
int pieceWidth = originalImage.getWidth() / 8;
// 繪制原始圖像
Graphics2D graphics2D = captchaImage.createGraphics();
graphics2D.drawImage(originalImage, 0, 0, null);
// 在原始位置繪制一個(gè)帶形狀的片段
graphics2D.setComposite(AlphaComposite.Clear);
graphics2D.fillRoundRect(captchaPosition, captchaImage.getHeight() / 2, pieceWidth, pieceWidth, 10, 10);
graphics2D.dispose();
return captchaImage;
}
private BufferedImage readImageFromResources() throws IOException {
InputStream inputStream = getClass().getResourceAsStream(Paths.get("path_to_resources", "originalImage.png").toString());
return ImageIO.read(inputStream);
}
private static int generateRandomCaptchaPosition(int width) {
Random random = new Random();
return random.nextInt(width / 2) + width / 4; // 將片段定位到圖像的中央位置
}
public int getCaptchaPosition() {
return captchaPosition;
}
}
這個(gè) ImageService 類實(shí)現(xiàn)了 createCaptchaImage 方法,這個(gè)方法可以生成一個(gè)滑動(dòng)拼圖驗(yàn)證碼。首先,它讀取了一個(gè)原始的圖像文件,然后隨機(jī)生成了一個(gè)驗(yàn)證碼的位置。接著,它創(chuàng)建了一個(gè)新的 BufferedImage 對(duì)象,將原始圖像繪制到了這個(gè)新的對(duì)象上,然后在驗(yàn)證碼位置上繪制了一個(gè)透明的缺口。
前端:驗(yàn)證碼的顯示和操作
前端使用HTML和JavaScript來顯示驗(yàn)證碼圖片和處理用戶的操作。核心代碼如下:
<img id="captcha" src="api/captcha" />
<div id="slider"><div id="knob"></div></div>
<script>
const slider = document.getElementById('slider');
const knob = document.getElementById('knob');
knob.onmousedown = function(event) {
event.preventDefault();
let shiftX = event.clientX - knob.getBoundingClientRect().left;
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
function onMouseMove(event) {
let newLeft = event.clientX - shiftX - slider.getBoundingClientRect().left;
if (newLeft < 0) newLeft = 0;
let rightEdge = slider.offsetWidth - knob.offsetWidth;
if (newLeft > rightEdge) newLeft = rightEdge;
knob.style.left = newLeft + 'px';
}
function onMouseUp() {
document.removeEventListener('mouseup', onMouseUp);
document.removeEventListener('mousemove', onMouseMove);
validateCaptcha(parseInt(knob.style.left));
}
};
knob.ondragstart = function() { return false; }
function validateCaptcha(position) {
fetch('api/validate?position=' + position, {method: 'POST'})
.then(response => response.json())
.then(data => { /* process the validation result */ });
}
</script>
在這段代碼中,顯示驗(yàn)證碼的元素從后端請(qǐng)求驗(yàn)證碼圖片?;瑒?dòng)條(<div id="slider">)和滑塊(<div id="knob">)用來讓用戶進(jìn)行操作。當(dāng)用戶按下滑塊時(shí),開始記錄滑塊的位置,當(dāng)用戶釋放滑塊時(shí),將滑塊的位置發(fā)送給后端進(jìn)行驗(yàn)證。
實(shí)戰(zhàn)應(yīng)用:滑動(dòng)拼圖驗(yàn)證碼的應(yīng)用示例
滑動(dòng)拼圖驗(yàn)證碼是一種常見的人機(jī)驗(yàn)證工具,主要用于區(qū)別用戶是人還是機(jī)器人。其操作方式通常是,用戶通過拖動(dòng)滑塊,將一個(gè)圖形拼圖從初始位置移動(dòng)到一個(gè)預(yù)定的位置,如此一來,系統(tǒng)就可以通過這種操作行為來判斷用戶是否為人類。下面是一個(gè)具體的應(yīng)用示例:
假如你正在構(gòu)建一個(gè)網(wǎng)站,希望在用戶注冊(cè)或登錄時(shí),通過滑動(dòng)拼圖驗(yàn)證碼來增強(qiáng)系統(tǒng)的安全防護(hù)。在用戶填寫完用戶名和密碼后,必須要求他們通過滑動(dòng)拼圖驗(yàn)證碼的驗(yàn)證,才能進(jìn)一步操作。
- 用戶首先訪問你的網(wǎng)站,并點(diǎn)擊注冊(cè)或登錄按鈕。
- 用戶在相應(yīng)的輸入框內(nèi)填入用戶名和密碼。
- 此時(shí),網(wǎng)站會(huì)顯示出一個(gè)滑動(dòng)拼圖驗(yàn)證碼,同時(shí),網(wǎng)站也會(huì)提示用戶需要通過拖動(dòng)滑塊,將拼圖移動(dòng)到正確的位置。
- 用戶根據(jù)提示操作,移動(dòng)滑塊,將拼圖還原到指定位置。
- 用戶成功完成滑動(dòng)拼圖驗(yàn)證碼后,就可以繼續(xù)注冊(cè)或登錄操作。
引入滑動(dòng)拼圖驗(yàn)證碼,可以有效增強(qiáng)你的網(wǎng)站對(duì)惡意機(jī)器人的防護(hù)能力,從而提高用戶數(shù)據(jù)的安全性。同時(shí),這種方式也很友好,用戶體驗(yàn)良好,是一種十分實(shí)用的人機(jī)交互方式。
本文詳細(xì)而深入地介紹了滑動(dòng)拼圖驗(yàn)證碼的工作原理及其在實(shí)戰(zhàn)中的應(yīng)用。文章首先解釋了什么是滑動(dòng)拼圖驗(yàn)證碼,它如何幫助識(shí)別用戶是否為人類,提高系統(tǒng)的安全性。然后,文章通過一個(gè)具體的實(shí)例,詳細(xì)地描述了如何在用戶注冊(cè)和登錄的過程中,使用滑動(dòng)拼圖驗(yàn)證碼增強(qiáng)網(wǎng)站的安全防護(hù)能力??偨Y(jié)而言,滑動(dòng)拼圖驗(yàn)證碼是一種有效且用戶體驗(yàn)良好的防護(hù)機(jī)制,能夠針對(duì)惡意機(jī)器人采取防護(hù)措施,提高用戶數(shù)據(jù)的安全性。