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.hqwc.cn/news/691773.html

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

相关文章

Python使用Rembg库去除图片背景

一、引入Rembg库 #库地址 https://github.com/danielgatis/rembg#CPU使用 pip install rembg # for library pip install rembg[cli] # for library cli#GPU使用&#xff08;系统支持onnxruntime-gpu&#xff09; pip install rembg[gpu] # for library pip install rembg[gp…

网络爬虫概述与原理

网络爬虫概述与原理 网络爬虫简介狭义上理解功能上理解常见用途总结 网络爬虫分类通用网络爬虫聚焦网络爬虫增量网络爬虫深度网络爬虫 网络爬虫流程网络爬虫采集策略深度有限搜索策略广度优先搜索策略 网络爬虫简介 通过有效地获取网络资源的方式&#xff0c;便是网络爬虫。网…

具身智能论文

目录 1. PoSE: Suppressing Perceptual Noise in Embodied Agents for Enhanced Semantic Navigation2. Embodied Intelligence: Bionic Robot Controller Integrating Environment Perception, Autonomous Planning, and Motion Control3. Can an Embodied Agent Find Your “…

分布式存储故障导致数据库无法启动故障处理---惜分飞

国内xx医院使用了国外医疗行业龙头的pacs系统,由于是一个历史库,存放在分布式存储中,由于存储同时多个节点故障,导致数据库多个文件异常,数据库无法启动,三方维护人员尝试通通过rman归档进行应用日志,结果发现日志有损坏报ORA-00354 ORA-00353,无法记录恢复,希望我们给予支持 M…

判断上三角矩阵 分数 15

题目展示&#xff1a; 代码展示&#xff1a; 点这里&#xff0c;输入题目名称即可检索更多题目答案 ​#include<stdio.h>int main() {//T-tint t 0;scanf("%d",&t);while(t--)//循环t次&#xff0c;处理t个矩阵{int n 0;scanf("%d",&n);…

JVM从1%到99%【精选】-类加载子系统

目录 1.类的生命周期 1.加载 2.连接 3.初始化 2.类的加载器 1.类加载器的分类 2.双亲委派机制 3.面试题&#xff1a;类的双亲委派机制是什么&#xff1f; 4.打破双亲委派机制 1.类的生命周期 类加载过程&#xff1a;加载、链接&#xff08;验证、准备、解析&a…

第五十二周:文献阅读+STHTNN

目录 摘要 Abstract 文献阅读&#xff1a;用于区域空气质量预测的时空分层传输神经网络 现有问题 提出方法 创新点 方法论 周期特征提取组件(PFEC) 场景动态图模块(SDGM) 时空特征提取组件&#xff08;STEC) 传输注意力模块(TransATT) STHTNN模型 研究实验 数据集…

03、SpringBoot 源码分析 - SpringApplication启动流程三

SpringBoot 源码分析 - SpringApplication启动流程三 初始化基本流程SpringApplication的setListeners设置监听器deduceMainApplicationClass对端主启动类rungetRunListeners获取SpringApplicationRunListener监听器EventPublishingRunListener的构造方法SimpleApplicationEven…

解决 Content type ‘application/json;charset=UTF-8‘ not supported

文章目录 问题描述原因分析解决方案参考资料 问题描述 我项目前端采用vue-elementUi-admin框架进行开发&#xff0c;后端使用SpringBoot&#xff0c;但在前后端登录接口交互时&#xff0c;前端报了如下错误 完整报错信息如下 前端登录接口JS代码如下 export function login(…

JVM堆内存分析

jmap工具查看堆内存 jmap:全称JVM Memory Map 是一个可以输出所有内存中对象的工具&#xff0c;可以将JVM中的heap&#xff08;堆&#xff09;&#xff0c;以二进制输出成文本&#xff0c;打印出Java进程对应的内存 找到pid jmap -heap 19792 Attaching to process ID 19792…

Word应用:一键提取手写签名

1、将带有签名的图片插入到word文档中&#xff0c;裁剪出签名部分&#xff1b; 2、点击“格式-颜色”,选择“重新着色”中的“黑白50%”&#xff1b; 3、“格式-颜色”&#xff0c;设置透明色&#xff1b; 4、选择“文件”选项卡&#xff0c;选择打开“选项”&#xff0c;点击“…

【动态规划】子序列问题I|最长递增子序列|摆动序列|最长递增子序列的个数|最长数对链

一、最长递增子序列 300. 最长递增子序列 算法原理&#xff1a; &#x1f4a1;细节&#xff1a; 1.注意子序列和子数组的区别&#xff1a; (1)子序列&#xff1a;要求顺序是固定的&#xff08;要求没那么高&#xff0c;所以子序列就多一些&#xff09; (2)子数组&#xff1a;要…