SpringBoot+Vue实现图片滑块和文字点击验证码
一、背景
1.1 概述
传统字符型验证码展示-填写字符-比对答案的流程,目前已可被机器暴力破解,应用程序容易被自动化脚本和机器人攻击。
摒弃传统字符型验证码,采用行为验证码采用嵌入式集成方式,接入方便,安全,高效。验证码展示-采集用户行为-分析用户行为流程,用户只需要产生指定的行为轨迹,不需要键盘手动输入,极大优化了传统验证码用户体验不佳的问题;同时,快速、准确的返回人机判定结果。
1.2 应用场景
- 网站登录:保护用户账号免受非法登录尝试
- 在线表单提交:避免垃圾邮件和恶意数据填充
- 论坛或社区:防止机器人自动发帖和灌水
- 支付验证:保障交易安全,防止欺诈行为
二、anji-plus
AJ-Captcha行为验证码,包含滑动拼图、文字点选两种方式,UI支持弹出和嵌入两种方式。后端提供Java实现,前端提供了php、angular、html、vue、uni-app、flutter、android、ios等代码示例。
代码开源地址:https://gitee.com/anji-plus/captcha
文档地址:https://ajcaptcha.beliefteam.cn/captcha-doc/
2.1 功能简介
功能 | 描述 |
---|---|
验证码类型 | 滑动拼图 blockPuzzle / 文字点选 clickWord |
验证 | 用户拖动/点击一次验证码拼图即视为一次“验证”,不论拼图/点击是否正确 |
二次校验 | 验证数据随表单提交到后台后,后台需要调用captchaService.verification做二次校验。目的是核实验证数据的有效性。 |
2.2 交互流程
① 用户访问应用页面,请求显示行为验证码
② 用户按照提示要求完成验证码拼图/点击
③ 用户提交表单,前端将第二步的输出一同提交到后台
④ 验证数据随表单提交到后台后,后台需要调用captchaService.verification做二次校验。
⑤ 第4步返回校验通过/失败到产品应用后端,再返回到前端。如下图所示。
三、代码实现
3.1 引入依赖
<dependency><groupId>com.anji-plus</groupId><artifactId>spring-boot-starter-captcha</artifactId><version>1.3.0</version>
</dependency>
3.2 配置
# 验证码配置
aj:captcha:########## 重点关注 ########### 验证码类型:default,blockPuzzle,clickWordtype: default# 底图路径,支持全路径、项目资源路径jigsaw: classpath:images/jigsaw# 滑动图路径,支持全路径、项目资源路径pic-click: classpath:images/pic-click# 缓存类型:local,redis,othercache-type: redis# local缓存的阈值,达到这个值,清除缓存cache-number: 1000# local定时清除过期缓存(单位秒),设置为0代表不执行timing-clear: 180# 滑块验证码的偏移量slip-offset: 5# 滑块验证码的加密坐标aes-status: true# 滑块验证码的滑块干扰项interference-options: 2# 文字验证码的文字数量【暂不可用】click-word-count: 4# 文字验证码的文字字体font-type: WenQuanZhengHei.ttf# 文字验证码的字体样式font-style: 1# 文字验证码的字体大小font-size: 25# 水印文字water-mark: 强哥软件# 水印文字字体water-font: WenQuanZhengHei.ttf########## 接口相关配置 ########### 历史数据清理是否开启history-data-clear-enable: false# 接口请求次数一分钟限制是否开启req-frequency-limit-enable: true# 验证失败5次,get接口锁定req-get-lock-limit: 5# 验证失败后,锁定时间间隔,sreq-get-lock-seconds: 360# get接口一分钟内请求数限制req-get-minute-limit: 30# check接口一分钟内请求数限制req-check-minute-limit: 60# verify接口一分钟内请求数限制req-verify-minute-limit: 60
3.3 自定义验证码存储
使用redis存储验证码
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
实现CaptchaCacheService 接口
package com.qiangesoft.captcha.cache;import com.anji.captcha.service.CaptchaCacheService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;import java.util.concurrent.TimeUnit;/*** redis缓存验证码** @author qiangesoft* @date 2024-05-10*/
public class CaptchaCacheServiceRedisImpl implements CaptchaCacheService {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Overridepublic void set(String key, String value, long expiresInSeconds) {stringRedisTemplate.opsForValue().set(key, value, expiresInSeconds, TimeUnit.SECONDS);}@Overridepublic boolean exists(String key) {return stringRedisTemplate.hasKey(key);}@Overridepublic void delete(String key) {stringRedisTemplate.delete(key);}@Overridepublic String get(String key) {return stringRedisTemplate.opsForValue().get(key);}@Overridepublic Long increment(String key, long val) {return stringRedisTemplate.opsForValue().increment(key, val);}@Overridepublic String type() {return "redis";}
}
配置
在resources目录新建META-INF.services文件夹,新建文件名为com.anji.captcha.service.CaptchaCacheService,内容为com.qiangesoft.captcha.cache.CaptchaCacheServiceRedisImpl
3.4 登录验证接口
package com.qiangesoft.captcha.controller;import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import com.qiangesoft.captcha.pojo.LoginDTO;
import com.qiangesoft.captcha.pojo.ResultVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;/*** 登录接口** @author qiangesoft* @date 2024-05-10*/
@Controller
public class LoginController {@Autowiredprivate CaptchaService captchaService;@GetMapping("/")public String index() {return "login";}@ResponseBody@PostMapping("/login")public ResultVO login(@RequestBody LoginDTO loginDTO) {// 登录二次校验CaptchaVO captchaVO = new CaptchaVO();captchaVO.setCaptchaVerification(loginDTO.getCaptcha());ResponseModel response = captchaService.verification(captchaVO);if (!response.isSuccess()) {throw new RuntimeException("图片验证码校验失败");}// todo 认证逻辑return ResultVO.ok();}}
3.5 vue方式
主要代码如下:
<template><div class="login-bg"><el-form style="width: 500px;height: 40px;margin: auto"><el-form-item label="账号" prop="username"><el-input v-model="username" placeholder="账号"></el-input></el-form-item><el-form-item label="密码" prop="password"><el-input v-model="password" placeholder="密码"></el-input></el-form-item><el-form-item><el-button type="primary" @click="checkParam">登录</el-button></el-form-item></el-form><Verifyref="verify"captcha-type="blockPuzzle":img-size="{width:'400px',height:'200px'}"@success="login"/><!-- <Verify--><!-- ref="verify"--><!-- captcha-type="clickWord"--><!-- :img-size="{width:'400px',height:'200px'}"--><!-- @success="login"--><!-- />--></div>
</template><script>
import Verify from './../components/verifition/Verify'
import { Message } from 'element-ui'
import { login } from '@/api'export default {components: {Verify},data () {return {username: 'admin',password: '123456'}},beforeDestroy () {document.removeEventListener('keyup', this.handlerKeyup)},created () {document.addEventListener('keyup', this.handlerKeyup)},methods: {handlerKeyup (e) {const keycode = document.all ? event.keyCode : e.whichif (keycode === 13) {this.checkParam()}},checkParam () {if (!this.username || !this.password) {Message.error('请先输入账号密码')}this.$refs.verify.show()},login (params) {login({username: this.username,password: this.password,captcha: params.captchaVerification}).then(res => {const code = res.codeif (code === 200) {Message.success('登录成功')this.$router.push('/index')} else {Message.error(res.message)}})}}
}
</script>
3.6 html方式
引入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
配置静态资源
package com.qiangesoft.captcha.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** 全局异常处理** @author qiangesoft* @date 2024-03-19*/
@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");}
}
html代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta name="viewport"content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/><title>verify插件demo</title><link rel="stylesheet" type="text/css" th:href="@{/static/css/verify.css}"><script>if (!window.Promise) {document.writeln('<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-promise/4.1.1/es6-promise.min.js"><' + '/' + 'script>');}</script><style>.btn {border: none;outline: none;width: 110px;height: 40px;line-height: 40px;text-align: center;cursor: pointer;background-color: #409EFF;color: #fff;font-size: 16px;}</style>
</head><body>
<div class="box"><h3>用户登录</h3>账号:<input type="text" id="username" placeholder="账号" value="admin"/> <br/><br/>密码:<input type="password" id="password" placeholder="密码" value="123456"/><br/><br/><button class="btn" id='btn'>滑块登录</button><button class="btn" id='btn1'>文字登录</button><div id="mpanel" style="margin-top:50px;"></div><div id="mpanel1" style="margin-top:50px;"></div>
</div><script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/jquery/1.9.1/jquery.js"></script>
<script th:src="@{/static/js/crypto-js.js}"></script>
<script th:src="@{/static/js/ase.js}"></script>
<script th:src="@{/static/js/verify.js}"></script><script>// 滑块$('#mpanel').slideVerify({baseUrl: 'http://localhost:8028',mode: 'pop',containerId: 'btn',imgSize: {width: '400px',height: '200px',},barSize: {width: '400px',height: '40px',},// 检验参数合法性的函数,mode ="pop"有效beforeCheck: function () {// todo 可进行参数校验return true},// 加载完毕的回调ready: function () {},// 成功的回调success: function (params) {const username = $("#username").val();const password = $('#password').val();$.ajax({url: '/login',type: 'post',contentType: 'application/json',dataType: 'json',data: JSON.stringify({"username": username,"password": password,"captcha": params.captchaVerification}),success: function (res) {if (res.code === 200) {alert("登录成功");} else {alert(res.message);}},error: function (e) {alert('请求失败')}})},// 失败的回调error: function () {}});// 文字点击$('#mpanel1').pointsVerify({baseUrl: 'http://localhost:8028',containerId: 'btn1',mode: 'pop',imgSize: {width: '400px',height: '200px',},beforeCheck: function () {return true},ready: function () {},success: function (params) {const username = $("#username").val();const password = $('#password').val();$.ajax({url: '/login',type: 'post',contentType: 'application/json',dataType: 'json',data: JSON.stringify({"username": username,"password": password,"captcha": params.captchaVerification}),success: function (res) {if (res.code === 200) {alert("登录成功");} else {alert(res.message);}},error: function (e) {alert('请求失败')}})},error: function () {}});
</script>
</body></html>
四、测试
4.1 接口调用
依赖中默认接口
功能 | 描述 | 请求方式 |
---|---|---|
获取验证码 | /captcha/get | post |
核对验证码 | /captcha/check | post |
接口调用流程
获取验证码接口:/captcha/get
请求参数
{"captchaType": "blockPuzzle", // 验证码类型 clickWord"clientUid": "唯一标识" // 客户端UI组件id,组件初始化时设置一次,UUID(非必传参数)
}
响应数据
{"repCode": "0000","repData": {"originalImageBase64": "底图base64","point": { // 默认不返回的,校验的就是该坐标信息,允许误差范围"x": 205,"y": 5},"jigsawImageBase64": "滑块图base64","token": "71dd26999e314f9abb0c635336976635", // 一次校验唯一标识"secretKey": "16位随机字符串", // aes秘钥,开关控制,前端根据此值决定是否加密"result": false,"opAdmin": false},"success": true,"error": false
}
核对验证码接口:/captcha/check
请求参数
{"captchaType": "blockPuzzle","pointJson": "QxIVdlJoWUi04iM+65hTow==", // aes加密坐标信息"token": "71dd26999e314f9abb0c635336976635" // get请求返回的token
}
响应数据
{"repCode": "0000","repData": {"captchaType": "blockPuzzle","token": "71dd26999e314f9abb0c635336976635","result": true,"opAdmin": false},"success": true,"error": false
}
4.2 vue
4.3 html
五、源码地址
码云:https://gitee.com/qiangesoft/boot-business/tree/master/boot-business-captcha
方便的话博客点个小心心,码云仓库点个star呗!!!
相关文章:

SpringBoot+Vue实现图片滑块和文字点击验证码
一、背景 1.1 概述 传统字符型验证码展示-填写字符-比对答案的流程,目前已可被机器暴力破解,应用程序容易被自动化脚本和机器人攻击。 摒弃传统字符型验证码,采用行为验证码采用嵌入式集成方式,接入方便,安全&#…...

每日复盘-20240515
仅用于记录当天的市场情况,用于统计交易策略的适用情况,以便程序回测 短线核心:不参与任何级别的调整,采用龙空龙模式 一支股票 10%的时候可以操作, 90%的时间适合空仓等待 国联证券 (1)|[9:25]|[133765万]|31.12 一…...

【Android】Apk图标的提取、相同目录下相同包名提取的不同图标apk但是提取结果相同的bug解决
一般安卓提取apk图标我们有两种常用方法: 1、如果已经获取到 ApplicationInfo 对象(假设名为 appInfo),那么我们获取方法为: appInfo.loadIcon(packageManager)// 返回一个 Drawable 对象2、 如果还没获取到 Applica…...

高校普法|基于SSM+vue的高校普法系统的设计与实现(源码+数据库+文档)
高校普法系统 目录 基于SSM+vue的高校普法系统的设计与实现 一、前言 二、系统设计 三、系统功能设计 1系统功能模块 2管理员功能模块 3律师功能模块 4学生功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获…...

pytest教程-47-钩子函数-pytest_sessionfinish
领取资料,咨询答疑,请➕wei: June__Go 上一小节我们学习了pytest_sessionstart钩子函数的使用方法,本小节我们讲解一下pytest_sessionfinish钩子函数的使用方法。 pytest_sessionfinish 钩子函数在 Pytest 测试会话结束时调用,…...

如何使用Python下载哔哩哔哩(Bilibili)视频字幕
在本文中,我将向大家展示如何使用Python下载哔哩哔哩(Bilibili)视频的字幕。通过这个方法,你可以轻松地获取你喜欢的视频的字幕文件,方便学习和交流。 准备工作 在开始之前,我们需要安装一些必要的库&…...

IP代理网络协议介绍
在IP代理页面上,存在HTTP/HTTPS/Socks5三种协议。它们都是客户端与服务器之间交互的协议。 HTTP HTTP又称之为超文本传输协议,在因特网使用范围广泛。它是一种请求/响应模型,客户端向服务器发送请求,服务器解析请求后对客户端作出…...
渗透相关面试+流量分析
文章目录 简单自我介绍上一个工作的主要内容Hvv的分组和流程你在hvv/攻防演练中取得了哪些成绩? 二、渗透相关面试题基础端口号以及入侵方式OSI七层协议响应状态码都有哪些?**WAF和IPS的区别**盲注是什么?java内存马类型**内存马有几种类型**…...

Shell之高效文本处理命令
目录 一、排序命令—sort 基本语法 常用选项 二、去重命令—uniq 基本语法 常用选项 三、替换命令—tr 基本语法: 常用选项 四、裁剪命令—cut 基本语法: 常用选项 字符串分片 五、拆分命令—split 基本语法: 六、 文件…...

u3d的ab文件注意事项
//----------------LoadAllAB.cs--------------------- using System.Collections;using UnityEngine;namespace System.IO{public class LoadAllAB : MonoBehaviour{ //读取本地string path "Assets/Actors/lznh/ab/animation/t_bl/";// Use this for initiali…...

Go微服务开源框架kratos的依赖注入关系总结
该文章为学习开源微服务框架kratos的学习笔记!官方文档见:简介 | Kratos Kratos 一套轻量级 Go 微服务框架,包含大量微服务相关框架及工具。 一、Kratos 项目结构简介 通过 Kratos 工具生成的 Go工程化项目模板如下: applicati…...
Linux 第三十二章
🐶博主主页:ᰔᩚ. 一怀明月ꦿ ❤️🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C,linux 🔥座右铭:“不要等到什么都没有了…...
手机号码的正则表达式
手机号码的正则表达式会根据不同的国家/地区有所不同,因为每个国家/地区都有自己特定的手机号码格式。但是,我可以为你提供一个通用的正则表达式模板,并给出一些具体国家/地区的例子。 通用模板 一个基本的手机号码正则表达式模板可能如下所…...

机器学习入门介绍
各位大佬好 ,这里是阿川的博客 , 祝您变得更强 个人主页:在线OJ的阿川 大佬的支持和鼓励,将是我成长路上最大的动力 阿川水平有限,如有错误,欢迎大佬指正 目录 三大方向机器学习产生的原因机器如何学习…...

一文说通用户故事点数是什么?
一文说通用户故事点数是什么? 第26期:一文说通用户故事点数是什么? 用户故事点数是一种采用相对估算法进行估算的一种工具,一般采用斐波那契数列表征用户故事里说的大小,采用0 1 2 3 5 8 13这样的一些数字来表征用户…...

GAME101-Lecture07学习
前言 今天主要讲shading(着色)。在讲着色前,要先讲图形中三角形出现遮挡问题的方法(深度缓存或缓冲)。 先采样再模糊错误:对信号的频谱进行翻译(在这期间会有频谱的混叠)ÿ…...

【一步一步了解Java系列】:了解Java与C语言的运算符的“大同小异”
看到这句话的时候证明:此刻你我都在努力~ 加油陌生人~ 个人主页: Gu Gu Study 专栏:一步一步了解Java 喜欢的一句话: 常常会回顾努力的自己,所以要为自己的努…...
ICSE docker related research
ICSE 2024 Empirical Study of the Docker Smells Impact on the Image Size Docker 气味对镜像大小影响的实证研究 Docker 是一种广泛采用的打包和部署应用程序的工具,它利用 Dockerfile 来构建镜像。然而,创建最佳的 Dockerfile 可能具有挑战性&…...

【C++】学习笔记——多态_1
文章目录 十二、继承8. 继承和组合 十三、多态1. 多态的概念2. 多态的定义和实现虚函数重写的两个特殊情况override 和 final 3. 多态的原理1. 虚函数表 未完待续 十二、继承 8. 继承和组合 我们已经知道了什么是继承,那组合又是什么?下面这种情况就是…...

C++map容器关联式容器
Cmap 1. 关联式容器 vector、list、deque、forward_list(C11)等STL容器,其底层为线性序列的数据结构,里面存储的是元素本身,这样的容器被统称为序列式容器。而map、set是一种关联式容器,关联式容器也是用来存储数据的࿰…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一:HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二:Floyd 快慢指针法(…...