滑块验证完整实现部署流程(前端 + 后端 + Nginx 集成)
一、滑块验证功能概述
滑块验证是一种常用的人机识别机制,通过要求用户拖动滑块完成指定轨迹操作,区分真实用户与自动化脚本请求,有效降低接口被恶意刷取的风险。完整的滑块验证实现需要前端交互层、后端校验层、反向代理层协同工作,以下为全流程实现方案。
二、前端实现(以Vue3为例)
2.1 核心交互逻辑
前端需要完成滑块组件的渲染、用户拖动事件监听、轨迹信息采集、请求发送与结果处理四个核心步骤,具体实现如下:
// 滑块验证组件核心逻辑
import { ref, onMounted } from 'vue'
import axios from 'axios'
// 滑块位置、轨迹、验证状态相关变量
const sliderPosition = ref(0)
const trackList = ref([])
const isVerified = ref(false)
const isDragging = ref(false)
const startX = ref(0)
const backgroundImg = ref('')
const sliderImg = ref('')
const captchaId = ref('')
// 初始化验证码,从后端获取验证素材
const initCaptcha = async () => {
try {
const res = await axios.get('https://www.ipipp.com/api/captcha/init')
backgroundImg.value = res.data.background_img
sliderImg.value = res.data.slider_img
captchaId.value = res.data.captcha_id
trackList.value = []
sliderPosition.value = 0
isVerified.value = false
} catch (err) {
console.error('初始化验证码失败', err)
}
}
// 开始拖动事件
const handleDragStart = (e) => {
isDragging.value = true
startX.value = e.type.includes('mouse') ? e.clientX : e.touches[0].clientX
trackList.value.push({
x: 0,
y: 0,
t: Date.now()
})
}
// 拖动中事件
const handleDragMove = (e) => {
if (!isDragging.value) return
const currentX = e.type.includes('mouse') ? e.clientX : e.touches[0].clientX
const offsetX = currentX - startX.value
// 限制滑块移动范围在0-280px之间
const newPosition = Math.max(0, Math.min(offsetX, 280))
sliderPosition.value = newPosition
trackList.value.push({
x: newPosition,
y: 0,
t: Date.now()
})
}
// 拖动结束事件,提交验证
const handleDragEnd = async () => {
if (!isDragging.value) return
isDragging.value = false
try {
const res = await axios.post('https://www.ipipp.com/api/captcha/verify', {
captcha_id: captchaId.value,
position: sliderPosition.value,
track: trackList.value
})
if (res.data.code === 200) {
isVerified.value = true
alert('验证通过')
} else {
alert('验证失败,请重试')
initCaptcha()
}
} catch (err) {
console.error('验证请求失败', err)
initCaptcha()
}
}
onMounted(() => {
initCaptcha()
})2.2 组件模板结构
前端模板需要包含背景图、滑块、轨迹采集容器等元素,结构如下:
<template>
<div class="captcha-container">
<div class="background-wrap">
<img :src="backgroundImg" class="background-img">
<img
:src="sliderImg"
class="slider-img"
:style="{ left: sliderPosition + 'px' }"
>
</div>
<div
class="slider-track"
@mousedown="handleDragStart"
@mousemove="handleDragMove"
@mouseup="handleDragEnd"
@mouseleave="handleDragEnd"
@touchstart="handleDragStart"
@touchmove="handleDragMove"
@touchend="handleDragEnd"
>
<div
class="slider-btn"
:style="{ left: sliderPosition + 'px' }"
>
<span v-if="!isVerified">→</span> <span v-else>✓</span>
</div>
<div class="track-text">请拖动滑块完成验证</div>
</div>
<button v-if="isVerified" @click="initCaptcha">刷新验证</button>
</div>
</template>三、后端实现(以Java Spring Boot为例)
3.1 核心依赖与配置
后端需要包含验证码生成、轨迹校验、缓存管理三个核心模块,首先引入必要依赖:
<!-- pom.xml 核心依赖 --> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.25</version> </dependency> </dependencies>
3.2 验证码初始化接口
生成随机验证码ID、背景图、滑块图,并将正确位置与验证信息存入Redis,代码如下:
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@RestController
public class CaptchaController {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 验证码缓存前缀
private static final String CAPTCHA_KEY_PREFIX = "captcha:";
// 验证码有效期5分钟
private static final long CAPTCHA_EXPIRE_TIME = 5;
@GetMapping("/api/captcha/init")
public Map<String, Object> initCaptcha() {
Map<String, Object> result = new HashMap<>();
// 生成唯一验证码ID
String captchaId = IdUtil.simpleUUID();
// 随机生成正确滑块位置(100-200px之间)
int correctPosition = RandomUtil.randomInt(100, 200);
// 模拟生成背景图与滑块图地址,实际场景可调用图片裁剪工具生成
String backgroundImg = "https://www.ipipp.com/static/captcha/bg_" + RandomUtil.randomInt(1, 10) + ".jpg";
String sliderImg = "https://www.ipipp.com/static/captcha/slider_" + RandomUtil.randomInt(1, 5) + ".png";
// 存入Redis的验证信息
Map<String, Object> captchaInfo = new HashMap<>();
captchaInfo.put("correct_position", correctPosition);
captchaInfo.put("expire_time", System.currentTimeMillis() + CAPTCHA_EXPIRE_TIME * 60 * 1000);
redisTemplate.opsForHash().putAll(CAPTCHA_KEY_PREFIX + captchaId, captchaInfo);
redisTemplate.expire(CAPTCHA_KEY_PREFIX + captchaId, CAPTCHA_EXPIRE_TIME, TimeUnit.MINUTES);
result.put("code", 200);
result.put("captcha_id", captchaId);
result.put("background_img", backgroundImg);
result.put("slider_img", sliderImg);
return result;
}
}3.3 验证接口实现
校验用户提交的滑块位置与轨迹是否合法,包括位置误差校验、轨迹时间合理性校验:
import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RestController
public class CaptchaVerifyController {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 位置允许误差范围
private static final int POSITION_ERROR_RANGE = 3;
// 轨迹最小点数
private static final int MIN_TRACK_COUNT = 5;
@PostMapping("/api/captcha/verify")
public Map<String, Object> verifyCaptcha(@RequestBody Map<String, Object> params) {
Map<String, Object> result = new HashMap<>();
String captchaId = (String) params.get("captcha_id");
Integer userPosition = (Integer) params.get("position");
String trackStr = JSONUtil.toJsonStr(params.get("track"));
// 校验验证码ID是否存在
Map<Object, Object> captchaInfo = redisTemplate.opsForHash().entries("captcha:" + captchaId);
if (CollUtil.isEmpty(captchaInfo)) {
result.put("code", 400);
result.put("msg", "验证码已过期或不存在");
return result;
}
// 校验位置误差
int correctPosition = (int) captchaInfo.get("correct_position");
if (Math.abs(userPosition - correctPosition) > POSITION_ERROR_RANGE) {
result.put("code", 400);
result.put("msg", "验证位置错误");
return result;
}
// 校验轨迹合法性
JSONArray trackArray = JSONUtil.parseArray(trackStr);
if (trackArray.size() < MIN_TRACK_COUNT) {
result.put("code", 400);
result.put("msg", "轨迹信息不完整");
return result;
}
// 校验轨迹时间递增、速度合理性(排除瞬间完成等异常轨迹)
long lastTime = 0;
for (int i = 0; i < trackArray.size(); i++) {
JSONObject trackPoint = trackArray.getJSONObject(i);
long currentTime = trackPoint.getLong("t");
if (currentTime < lastTime) {
result.put("code", 400);
result.put("msg", "轨迹时间异常");
return result;
}
lastTime = currentTime;
}
// 验证通过,删除Redis中的验证码信息
redisTemplate.delete("captcha:" + captchaId);
result.put("code", 200);
result.put("msg", "验证通过");
return result;
}
}四、Nginx集成配置
Nginx作为反向代理层,需要处理前端静态资源转发、后端接口代理、防CC攻击等场景,核心配置如下:
# nginx.conf 滑块验证相关配置
server {
listen 80;
server_name www.ipipp.com;
# 前端静态资源部署
location / {
root /usr/share/nginx/html/frontend;
index index.html index.htm;
# 解决前端路由刷新404问题
try_files $uri $uri/ /index.html;
}
# 后端接口代理
location /api/ {
proxy_pass http://127.0.0.1:8080/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 限制单个IP的验证接口请求频率,防止暴力破解
limit_req zone=captcha_limit burst=5 nodelay;
}
# 验证码图片静态资源转发
location /static/captcha/ {
proxy_pass https://www.ipipp.com/static/captcha/;
expires 1d;
}
}
# 定义请求限制规则,每秒最多处理10个验证码相关请求
limit_req_zone $binary_remote_addr zone=captcha_limit:10m rate=10r/s;五、部署与测试流程
5.1 部署顺序
首先将后端Spring Boot项目打包为Jar包,上传至服务器,执行
java -jar captcha-service.jar启动服务,确保后端接口可正常访问将前端项目打包后的静态文件上传至服务器
/usr/share/nginx/html/frontend目录修改Nginx配置文件,执行
nginx -t校验配置正确性,之后执行nginx -s reload重载配置检查Redis服务是否正常运行,确认后端可正常连接Redis
5.2 功能测试要点
验证前端初始化时能否正常获取验证码素材,刷新后素材是否更新
拖动滑块到正确位置,确认返回验证通过结果
拖动滑块到错误位置,确认返回验证失败结果,且验证码刷新
模拟脚本请求验证接口,确认Nginx的请求限制生效,超出频率的请求被拦截
验证验证码过期后(5分钟),再次提交会提示验证码失效
六、注意事项
滑块验证仅为辅助防护手段,不能完全替代其他安全措施,建议结合IP黑名单、接口签名校验、请求频率限制等多重机制提升系统安全性。同时,轨迹校验规则需要根据实际业务场景调整,避免过于严格导致真实用户验证失败,或过于宽松导致恶意脚本绕过。
如果需要在HTML页面中插入相关标签示例,比如描述表单元素时,应写:HTML表单是网页与用户交互的核心元素,而 <input> 标签常用于接收用户输入。不要直接使用未转义的标签名称。