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/getpost
核对验证码/captcha/checkpost

接口调用流程
在这里插入图片描述

获取验证码接口:/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呗!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/10866.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

光伏电站智能管理平台功能全面介绍

一、介绍 光伏电站智能管理平台专门为了光伏电站服务的融合了项目沟通、在线设计、施工管理、运维工单等多智能光伏管理系统&#xff0c;可以满足光伏电站建设前期沟通、中期建设和后续维护的一体化智能平台&#xff0c;同时通过组织架构对企业员工进行线上管理和数据同步&…

BUU-二维码

题目 解题 打开是一张图片&#xff0c;扫描二维码后显示 secret is here 一时没有思路&#xff0c;看别人的wp https://blog.csdn.net/wusimin432503/article/details/125692459https://blog.csdn.net/weixin_45728231/article/details/120988424?spm1001.2101.3001.6661.1…

显卡、显卡驱动、CUDA、cuDNN、CUDA Toolkit、NVCC、nvidia-smi等概念的区别与联系

在科技日新月异的今天&#xff0c;显卡、显卡驱动、CUDA、cuDNN、CUDA Toolkit、NVCC、nvidia-smi等术语已经成为了科技领域的重要组成部分。本文旨在阐述这些术语之间的区别与联系&#xff0c;帮助您更好地理解它们在技术生态系统中的作用。 一、显卡 显卡&#xff0c;也称为…

(动画详解)LeetCode面试题 02.04.分割链表

&#x1f496;&#x1f496;&#x1f496;欢迎来到我的博客&#xff0c;我是anmory&#x1f496;&#x1f496;&#x1f496; 又和大家见面了 欢迎来到动画详解LeetCode系列 用通俗易懂的动画的动画使leetcode算法题可视化 先来自我推荐一波 个人网站欢迎访问以及捐款 推荐阅读…

[AutoSar]BSW_Diagnostic_003 ReadDataByIdentifier(0x22)介绍

目录 关键词平台说明背景一、请求格式二、常用DID三、响应格式四、NRC五、case 关键词 嵌入式、C语言、autosar、OS、BSW、UDS、diagnostic 平台说明 项目ValueOSautosar OSautosar厂商vector &#xff0c; EB芯片厂商TI 英飞凌编程语言C&#xff0c;C编译器HighTec (GCC)au…

Linux环境下parted工具使用

在工作中&#xff0c;我们经常会遇到大于分区大于2T的磁盘&#xff0c;由于系统盘最大不能超2T&#xff0c;我们会在做raid时将划分VD来进行装系统&#xff0c;但系统自动安装后无法将磁盘全部识别出来&#xff0c;管理员有时会要求手动对分区进行挂载&#xff0c;这个文档介绍…

保研机试之【二叉树后序】--1道题

参考&#xff1a;东哥带你刷二叉树&#xff08;后序篇&#xff09; | labuladong 的算法笔记 建议先过一遍&#xff1a;今天是二叉树~-CSDN博客&#xff0c;very重要&#xff01; 然后再过一遍&#xff08;理解怎么应用方法&#xff09;&#xff1a;保研机试之[三道二叉树习题…

用python写算法——队列笔记

1.队列定义 队列是一种特殊的线性表&#xff0c;它只允许在表的前端进行删除操作&#xff0c;在表的后端进行插入操作&#xff0c;和栈一样&#xff0c;队列是一种操作受限制的线性表。进行插入操作的端称为队尾&#xff0c;进行删除操作的端称为队头。队列中没有元素时&#…

【初级数据结构】队列

目录 前言队列的概念及结构队列的实现队列的结构队列的初始化队列的销毁入队出队取队头元素取队尾元素判断队列是否为空取出队列中元素个数代码测试 完整代码Queue.hQueue.ctest.c 前言 前面我们已经学习了栈&#xff0c;栈是一种后进先出的结构&#xff0c;即LIFO&#xff0c;…

前端工程化,前端监控,工作流,部署,性能

开发规范 创建项目的时候&#xff0c;配置下 ESlint&#xff0c;stylelint&#xff0c; prettier&#xff0c; commitlint 等; ESLint 主要功能&#xff1a; ESLint 是一个静态代码检查工具&#xff0c;用于在 JavaScript 代码中识别和报告模式。它的目标是提供一个插件化的 …

electron进程间通信

Electron 应用程序的结构非常相似。 作为应用开发者&#xff0c;你将控制两种类型的进程&#xff1a;主进程 和 渲染器进程。 这类似于上文所述的 Chrome 的浏览器和渲染器进程。 主进程 每个 Electron 应用都有一个单一的主进程&#xff0c;作为应用程序的入口点。 主进程在 N…

红黑树的理解和简单实现

目录 1. 红黑树的概念和性质 2. 红黑树的插入 2.1. 情况一&#xff1a;新增节点的父亲为空 2.2. 情况二&#xff1a;新增节点的父亲非空且为黑色节点 2.3. 情况三&#xff1a;当父亲为红节点&#xff0c;叔叔存在且为红 2.3.1. 当祖父为根节点的时候 2.3.2. 当祖父不是根…

AttributeError: module ‘PIL.Image‘ has no attribute ‘ANTIALIAS‘

问题描述 修改图片大小的时候&#xff0c;代码报错&#xff1a;AttributeError: module PIL.Image has no attribute ANTIALIAS 解决方案 在pillow的10.0.0版本中&#xff0c;ANTIALIAS方法被删除了。 方法1&#xff1a;修改版本&#xff08;不推荐&#xff09; pip instal…

docker 方式 elasticsearch 8.13 简单例子

安装 docker 虚拟机安装 elastic search 安装本地 # 创建 elastic 的网络 docker network create elastic # 用镜像的方式创建并启动容器 docker run -d --name es --net elastic -p 9200:9200 -p 9300:9300 -e "discovery.typesingle-node" -e "xpack.secur…

嵌入式全栈开发学习笔记---C语言笔试复习大全16

目录 指针和数组 用指针来表示数组 用数组来表示指针 笔试题19 上一篇复习了指针使用时的相关注意事项&#xff0c;这一篇我们开始复习指针和数组。 说明&#xff1a;我们学过单片机的一般都是有C语言基础的了&#xff0c;网上关于C语言的资料有很多&#xff0c;大家如果对…

【半夜学习MySQL】数据库中的数据类型(含数值类型、文本二进制类型、时间类型、String类型详谈)

&#x1f3e0;关于专栏&#xff1a;半夜学习MySQL专栏用于记录MySQL数据相关内容。 &#x1f3af;每天努力一点点&#xff0c;技术变化看得见 文章目录 数据类型分类数值类型bit类型tinyint类型int类型float类型decimal类型 文本、二进制类型char类型varchar类型 时间类型Strin…

1.1. 离散时间鞅-条件期望

1.1. 离散时间鞅-条件期望 条件期望1. 条件期望的定义1.1. 条件期望的定义1.2. 条件期望的存在唯一性 2. 条件期望的示例2.1. X ∈ F X \in \mathcal{F} X∈F&#xff0c; X X X与 F \mathcal{F} F独立的情形2.2. X X X是有限 σ \sigma σ代数情形2.3. X X X是随机变量生成…

[Flutter GetX使用] Getx路由和状态管理-GetController使用过程中的踩坑记录

文章目录 问题 - Get.find() 报错!原因总结A:路由和控制器设计a1:项目中的Get路由aa1.项目路由结构aa2.本项目路由的注意点: B: GetController的冷知识C: 总结来看D: 一些参考资料 问题 - Get.find() 报错! 刚接触Getx, 遇到 Get.find()确找不到, 进而报错的问题, 一时间有点没…

智慧旅游平台开发微信小程序【附源码、文档说明】

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…

关于DDD和COLA的一些总结和思考

1|0思维&#xff1a;面向对象和面向过程 领域驱动设计本质上是讲的面向对象&#xff0c;但是谈面向对象&#xff0c;始终无法绕开面向过程&#xff0c;所以我们先好好说一下面向过程和面向对象这两个概念。 什么是面向过程呢&#xff0c;其实就是我们学习编程时最初被植入的逻辑…