SpringBoot+Vue实现图片滑块和文字点击验证码

news2024/9/20 0:50:45

一、背景

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,clickWord
    type: default
    # 底图路径,支持全路径、项目资源路径
    jigsaw: classpath:images/jigsaw
    # 滑动图路径,支持全路径、项目资源路径
    pic-click: classpath:images/pic-click
    # 缓存类型:local,redis,other
    cache-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
    # 验证失败后,锁定时间间隔,s
    req-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 {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void set(String key, String value, long expiresInSeconds) {
        stringRedisTemplate.opsForValue().set(key, value, expiresInSeconds, TimeUnit.SECONDS);
    }

    @Override
    public boolean exists(String key) {
        return stringRedisTemplate.hasKey(key);
    }

    @Override
    public void delete(String key) {
        stringRedisTemplate.delete(key);
    }

    @Override
    public String get(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    @Override
    public Long increment(String key, long val) {
        return stringRedisTemplate.opsForValue().increment(key, val);
    }

    @Override
    public 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 {

    @Autowired
    private 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>

    <Verify
      ref="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.which
      if (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.code
        if (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 {

    @Override
    public 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.coloradmin.cn/o/1666959.html

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

相关文章

速了解及使用布隆过滤器

布隆过滤器 介绍 概念&#xff1a;是一种高效查询的数据结构 作用&#xff1a;判断某个元素是否在一个集合中。&#xff08;但是会出现误判的情况&#xff09; 实现原理 加入元素&#xff1a; 当一个元素需要加入到布隆过滤器中时&#xff0c;会使用一组哈希函数对该元素进…

每周日发系统规划与管理师伴读脑图,今天是第4章

从第4章开始&#xff0c;系统规划与管理师的学习就正式步入了主题&#xff0c;考虑到我过去中断了2周&#xff0c;想必你的第4章教程已经看完了吧&#xff1f;

2024年天津市静海区教师招聘报名流程(建议电脑)

2024年天津市静海区教师招聘报名流程&#xff08;建议电脑&#xff09; #报名 #教师招聘 #教师招聘考试 #教招 #天津教师招聘 #天津教师招聘考试 #24年天津教师招聘 #24年天津市教师招聘考试 #天津市静海区教师招聘 #静海区教师招聘考试 #静海区教师编 #静海区#

【OceanBase诊断调优】—— 租户资源统计项及其查询方法

本文主要介绍 OceanBase 数据库中租户资源统计项及其查询方法。 适用版本 OceanBase 数据库 V4.1.x、V4.2.x 版本。 CPU 资源统计项 逻辑 CPU 使用率&#xff08;线程处理请求的时间占比&#xff09;。 通过虚拟表 __all_virtual_sysstat 在 SYS 系统租户下&#xff0c;查看…

Ubuntu意外断电vmdk损坏--打不开磁盘“***.vmdk”或它所依赖的某个快照磁盘。

背景&#xff1a;电脑资源管理器崩溃卡死&#xff0c;强行断电重启&#xff0c;结果虚拟机打不开了&#xff0c;提示打不开磁盘“***.vmdk”或它所依赖的某个快照磁盘。 删除lck文件&#xff1a;失败vmware-vdiskmanager修复 &#xff1a;提示无法修复最终用 VMFS Recovery挂载…

【机器学习】集成学习在信用评分领域实例

集成学习在信用评分领域的应用与实践 一、引言二、集成学习的概念与原理三、集成学习在信用评分中的应用实例四、总结与展望 一、引言 在当今金融数字化快速发展的时代&#xff0c;信用评分成为银行、金融机构等评估个人或企业信用风险的重要工具。然而&#xff0c;单一的信用评…

OFDM802.11a的FPGA实现(十二)使用FFT IP核添加循环前缀

原文链接&#xff08;相关文章合集&#xff09;&#xff1a;OFDM 802.11a的xilinx FPGA实现 目录 1.前言2.循环前缀3.硬件实现4.ModelSim仿真 1.前言 为了能够消除传输过程当中的符号间干扰&#xff0c;在IFFT处理完毕之后还要加上循环前缀。 2.循环前缀 实际通信信道中,由于接…

Linux常用软件安装(JDK、MySQL、Tomcat、Redis)

目录 一、上传与下载工具Filezilla1. filezilla官网 二、JDK安装1. 在opt中创建JDK目录2.上传JDK压缩文件到新建目录中3.卸载系统自代jdk4.安装JDK5.JDK环境变量配置6. 验证是否安装成功 三、安装MySQL1.创建mysql文件夹2.下载mysql安装压缩包3.上传到文件夹里面4. 卸载系统自带…

动态规划算法:⼦数组、⼦串系列(数组中连续的⼀段)

例题一 解法&#xff08;动态规划&#xff09;&#xff1a; 算法思路&#xff1a; 1. 状态表⽰&#xff1a; 对于线性 dp &#xff0c;我们可以⽤「经验 题⽬要求」来定义状态表⽰&#xff1a; i. 以某个位置为结尾&#xff0c;巴拉巴拉&#xff1b; ii. 以某个位置…

清除HP打印机内存的5种方法,总有一种适合你

序言 HP打印机通常具有2 MB到32 MB的内部内存容量。打印机使用此内存存储打印作业和信息,如文档中的页数、纸张类型、纸张大小和字体。但是,如果打印作业的大小超过打印机的内存大小,它将无法执行打印命令,并将拒绝打印文档。 此外,有时打印作业可能会卡在打印机的内存中…

Matlab/simulink永磁直驱风机的建模仿真

Matlab/simulink直驱永磁同步风机的建模仿真&#xff0c;跟随风速波动效果好&#xff0c;可以作为后期科研的基础模型

关于 IIS 开启匿名访问网站仍要账号密码登录网站的解决方法

欢迎关注公总号【云边小网安】 问题提出&#xff1a;发现虽然勾选了允许匿名访问网站&#xff0c;但在访问某一网站的时候仍然需要登录账号密码 解决方法一&#xff1a;登录管理员账号密码解决方法二&#xff1a;添加访问网站文件夹的用户 访问某一网站本质上来讲&#xff0…

Adobe Animate 2024软件下载

Adobe Animate 2024软件下载&#xff1a; 百度网盘下载https://pan.baidu.com/s/1cQQCFL16OUY1G6uQWgDbSg?pwdSIMS Adobe Animate 2024&#xff0c;作为Flash技术的进化顶点&#xff0c;是Adobe匠心打造的动画与交互内容创作的旗舰软件。这款工具赋予设计师与开发者前所未有的…

避坑指南!RK3588香橙派yolov5生成RKNN模型!

地址1&#xff0c;转换模型 地址2&#xff0c;转换模型 地址3&#xff0c;解决ppa 下载k2 本文目录 一、将.pt模型转为onnx模型文件。&#xff08;Windows&#xff09;二、将.onnx模型转为.rknn模型文件。&#xff08;Linux&#xff09;三、将.rknn模型部署到开发板RK3588中…

SSRF服务器端请求伪造

漏洞原理 SSRF挖掘 SSRF具体利用 SSRF具体验证 SSRF防御与绕过 漏洞原理 这个漏洞允许攻击者去利用服务端的功能&#xff0c;来请求其他网络资源 SSRF(Server-Side Request Forgery:服务器端请求伪造) 是指攻击者能够从易受攻击的 Web应用程序发送精心设计的请求的对其他网站…

数列排序C++

题目&#xff1a; 思路&#xff1a; 创建一个数组a&#xff0c;循环遍历输入&#xff0c;然后使用函数sort进行上升排序&#xff0c;最后循环遍历输出a[i]. #include <bits/stdc.h> using namespace std; int main(){int a[201];int n;cin>>n;//输入for(int i0;i&l…

使用单片机在图形点阵LCD上绘制波形图

使用单片机在图形点阵LCD上绘制波形图 需求&#xff1a; 假如有一组浮点数据&#xff0c;是通过AD转换得到的&#xff0c;保存在数组MyArray[]中&#xff0c;采集点数为len&#xff0c;采集周期为T&#xff0c;现在想用单片机在LCD上绘制出这组数据对应的波形图&#xff0c;该…

python实现动态时钟功能

欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~ 一.前言 时钟,也被称为钟表,是一种用于测量、记录时间的仪器。时钟通常由时针、分针、秒针等计时仪器组成,是现代社会不可或缺的一种计时工具。它的发明和使用极大地改变了人类的生活方式和时间观念。 时钟的类型有很多,…

批量文本高效编辑神器:轻松拆分每行内容,一键保存更高效!轻松实现批量拆分与保存

文本处理成为我们日常工作中的一项重要任务。然而&#xff0c;面对大量的文本内容&#xff0c;传统的逐行编辑方式往往显得繁琐且效率低下。那么&#xff0c;有没有一种更高效、更便捷的解决方案呢&#xff1f;答案是肯定的——批量文本高效编辑神器&#xff0c;让您的文本处理…

torch_geometric安装(CPU版本)

①打开官方安装网址&#xff1a;https://pytorch-geometric.readthedocs.io/en/2.3.0/install/installation.html ②对根据Pytorch选择相应版本。此前一直用CUDA不成功&#xff0c;这次使用CPU版本&#xff08;因为不用对应cuda&#xff0c;pytorchcudageometric三者对应起来很…