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,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呗!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

train_gpt2_fp32.cu

源程序 llm.c/test_gpt2_fp32.cu at master karpathy/llm.c (github.com) #include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #include <assert.h> #include <float.h> #include <string.h> #include…

国内十大免费图床推荐

国内十大免费图床推荐 近期&#xff0c;莫卡乐AI导航站汇总了国内一些出色的图床网站&#xff0c;既有知名大站&#xff0c;也有小众网站&#xff0c;用户的使用体验都非常好&#xff01; 1.路过图床 地址&#xff1a;https://imgse.com/ 我们是国内知名的图床之一&#xf…

Windows只能安装在GPT磁盘上

转换磁盘分区形式 步骤1. 先按照正常流程使用Windows系统安装光盘或系统U盘引导计算机。 步骤2. 在Windows安装程序中点击“开始安装”&#xff0c;然后按ShiftF10打开命令提示符。 步骤3. 依次输入以下命令&#xff0c;并在每一行命令后按一次Enter键执行。 步骤4. 等待转换…

条件平差——以水准网平差为例 (python详细过程版)

目录 一、原理概述二、案例分析三、代码实现四、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、原理概述 条件平差的函数模型和随机模型为: A V + W = 0

Dbeaver network unavailable due to certificate issue

场景&#xff1a;出现在DBeaver连接数据库下载驱动的时候 解决&#xff1a; 别勾选就可以了

制冰机的分类介绍

制冰机分别有哪些类型&#xff1f;制冰机顾思义就是制作冰块的机器&#xff0c;但是冰块分片冰、块冰、管冰、颗粒冰等。根据制冰机制出冰块的形状&#xff0c;可以分为&#xff1a;片冰机、块冰机、管冰机、颗粒冰机、雪花机、板冰机、以及最新研制的球冰机等。 制冰机是采用制…

linux 安装 mangodb 并设置服务开机自启

1、下载 wget http://mosquitto.org/files/source/mosquitto-1.6.8.tar.gz 2、解压 tar -zxvf mosquitto-1.6.8.tar.gz 3、编译安装cd mosquitto-1.6.8 make sudo make install4、在当前目录。进入mosquitto服务文件存放的文件夹 cd service/systemd可以看到3个文件 点击read…

2024年旅游行业薪酬报告

来源&#xff1a;薪智 近期历史回顾&#xff1a; 2024年中国健康家电消费洞察及趋势研究报告.pdf 2024巴菲特股东大会5万字完整版.pdf 2024年全国大学生新媒体直播大赛.pdf 2024北京市高级别自动驾驶示范区数据安全治理白皮书.pdf 2024年第一季度开发者健康调查报告.pdf 2024年…

商务分析方法与工具(八):Python的趣味快捷-年少不知numpy好,再见才觉很简单

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

CentOS 磁盘扩容与创建分区

文章目录 未分配空间创建新分区重启服务器添加物理卷扩展逻辑卷 操作前确认已给服务器增加硬盘或虚拟机已修改硬盘大小&#xff08;必须重启服务才会生效&#xff09;。 未分配空间 示例说明&#xff1a;原服务器只有40G&#xff0c;修改虚拟机硬盘大小再增加20G后硬盘变为60G。…

OpenID Connect 是什么?和 OAuth 有哪些异同?

因为工作关系&#xff0c;我需要给一个业务网站配置一个 SSO&#xff0c;我一看&#xff0c;这个业务网站只支持 SAML 和 OpenID Connect&#xff0c;也即 OIDC。其实早就听说过这个词&#xff0c;但是没有仔细了解过。所以&#xff0c;特来学习一下到底什么是 OIDC。 一、 什…

【计算机网络】计算机网络的性能指标

&#x1f6a9;本文已收录至专栏&#xff1a;计算机网络学习之旅 计算机网络的性能指标被用来从不同方面度量计算机网络的性能。常用的八个计算机网络性能指标&#xff1a;速率、带宽、吞吐量、时延、时延带宽积、往返时间、利用率、丢包率。 一.速率 (1) 数据量 比特&#…

【论文笔记】DualBEV: CNN is All You Need in View Transformation

原文链接&#xff1a;https://arxiv.org/abs/2403.05402 1. 引言 有效的BEV目标检测需要PV到BEV的视图变换&#xff08;VT&#xff09;。目前的VT分为2D到3D和3D到2D两类&#xff0c;前者通过预测深度概率提升2D特征&#xff0c;但存在深度不确定性&#xff1b;后者则使用3D查…

动态规划解决回文子串问题

前言&#xff1a; 回文串相关问题在我们的算法题中算是老生常谈&#xff0c;本文主要介绍如何使用动态规划的思路去解决回文串系列问题。 总体思路&#xff1a; 能够将所有的子串是否是回文的信息&#xff0c;存储在二维dp表中。有了这个dp表&#xff0c;就可以将hard难度转…

信息系统安全与对抗-网络侦查技术与网络扫描技术(期末复习简答题)

1、网络拓扑结构在网络攻击中的作用 查明目标网络的拓扑结构&#xff0c;有利于找到目标网络的关键节点&#xff0c;从而提高攻击效率&#xff0c;达到最大攻击效果。 2、网络侦查在网络攻击中的作用 识别潜在目标系统&#xff0c;确认目标系统适合哪种类型的攻击。 3、百度…

一种简单的小报表本地缓存方案

适应如下场景&#xff1a;关联表多&#xff0c;接口响应慢&#xff0c;报表数据不多&#xff0c;可能就十多行。参数也固定&#xff0c;实时性要求不高&#xff0c;隔那么半小时刷新一次&#xff0c;查询性能要求高&#xff0c;给领导看的&#xff0c;要求很快。 使用示例&…

对camera raw中的纹理和清晰度的内容的修正(之前的内容写错了,懒得改了重新写一篇)

之前对于环的解释&#xff0c;不太行&#xff0c;这里我给出进一步地说明。 首先对环的解释: 我这里说的环指的是频域段中的ai变化的时候对图像像素的变化的极大的影响程度的环状效果&#xff0c;会出现不规则的环状的提亮或增暗的效果。实际上是每个fj都有影响&#xff0c;但…

【C/C++】设计模式——工厂模式:简单工厂、工厂方法、抽象工厂

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

光学镜片镀膜自动上下料工艺解决方案

在当今竞争激烈的制造业市场中&#xff0c;如何提高产品质量和生产效率成为了企业关注的焦点。富唯镀膜上下料设备以其高精度上下料技术&#xff0c;成为了产业升级的得力助手。 产品介绍 实现功能&#xff1a;富唯镀膜上下料设备拥有先进的设计理念和精湛的技术工艺&#xff…

邓超大胆自嘲让全场观众笑出眼泪

《哈哈哈哈哈》第四季中&#xff0c;邓超大胆自嘲&#xff0c;让全场观众笑出眼泪&#xff01;嫁到上海已久的他&#xff0c;还听不懂上海话&#xff0c;这让老婆孙俪也忍不住笑出声来。这期节目一播出&#xff0c;网友们纷纷表示&#xff1a;“超哥今晚还敢回家吗&#xff1f;…