SpringBoot 集成滑块验证码AJ-Captcha行为验证码 Redis分布式 接口限流 防爬虫

介绍

滑块验证码比传统的字符验证码更加直观和用户友好,能够很好防止爬虫获取数据。

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

效果图

依赖

xml 复制代码
 <dependency>
 	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
   <groupId>com.anji-plus</groupId>
   <artifactId>captcha-spring-boot-starter</artifactId>
   <version>1.4.0</version>
</dependency>

Redis配置

yml 复制代码
spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password:
    database: 1
    timeout: 6000

验证码配置

yml 复制代码
aj:
  captcha:
    # 滑动验证底图路径,不配置将使用默认图片
    jigsaw: classpath:images/jigsaw
    # 滑动验证底图路径,不配置将使用默认图片
    pic-click: classpath:images/pic-click

    # 缓存类型设置,默认使用local缓存
    cache-type: redis
    # local缓存的阈值, 达到此值后清除缓存
    cache-number: 1000
    # local定时清除过期缓存(单位秒), 设置为0表示不执行
    timing-clear: 180

    # 验证码类型,default代表两种都实例化
    type: default

    # 右下角水印文字,使用Unicode表示
    # 汉字统一使用Unicode,保证程序通过@value读取到是中文,可通过这个在线转换;yml格式不需要转换
    # https://tool.chinaz.com/tools/unicode.aspx 中文转Unicode
    water-mark: "\u6211\u7684\u6c34\u5370"  # Unicode: 我的水印

    # 水印字体(可选配置,默认为文泉驿正黑)
    # water-font: WenQuanZhengHei.ttf

    # 滑动拼图允许的误差偏移量(默认5像素)
    slip-offset: 5

    # AES加密坐标开启或者禁用 (true 或 false)
    aes-status: true

    # 滑动验证干扰项配置 (0、1、2)
    interference-options: 2

    # 点选验证码字体样式 (默认Font.BOLD)
    font-style: 1

    # 点选字体的大小
    font-size: 25

    # 历史数据清除配置,是否启用
    history-data-clear-enable: false

    # 接口请求频率限制配置
    req-frequency-limit-enable: false

    # 验证失败次数达到限制后,get接口将被锁定
    req-get-lock-limit: 5

    # 验证失败后,锁定时间间隔(秒)
    req-get-lock-seconds: 360

    # get接口一分钟内请求次数限制
    req-get-minute-limit: 30

    # check接口一分钟内请求次数限制
    req-check-minute-limit: 60

    # verify接口一分钟内请求次数限制
    req-verify-minute-limit: 60

配置类

java 复制代码
@Configuration
@RequiredArgsConstructor
public class CaptchaConfig {

    private  final StringRedisTemplate redisTemplate;

    @Bean(name = "AjCaptchaCacheService")
    @Primary
    public CaptchaCacheService captchaCacheService(AjCaptchaProperties config){
        //缓存类型redis/local/....
        CaptchaCacheService ret = CaptchaServiceFactory.getCache(config.getCacheType().name());
        if(ret instanceof CaptchaCacheServiceRedisImpl){
            ((CaptchaCacheServiceRedisImpl)ret).setStringRedisTemplate(redisTemplate);
        }
        return ret;
    }

    /**
     * 国际化配置
     * @return
     */
//    @Bean
//    @ConditionalOnMissingBean
//    public MessageSource messageSource() {
//        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
//        messageSource.setBasenames("messages/messages","captcha/messages");
//        messageSource.setDefaultEncoding("UTF-8");
//        return messageSource;
//    }
}

实现类

java 复制代码
/**
        * 对于分布式部署的应用,我们建议应用自己实现CaptchaCacheService,比如用Redis,参考service/spring-boot代码示例。
        * 如果应用是单点的,也没有使用redis,那默认使用内存。
        * 内存缓存只适合单节点部署的应用,否则验证码生产与验证在节点之间信息不同步,导致失败。
        *
        * ☆☆☆ SPI: 在resources目录新建META-INF.services文件夹(两层),参考当前服务resources。
        * @Title: 使用redis缓存
        * @author Devli
        * @date 2020-05-12
        */
public class CaptchaCacheServiceRedisImpl implements CaptchaCacheService {

    @Override
    public String type() {
        return "redis";
    }

    private static final String LUA_SCRIPT = "local key = KEYS[1] " +
            "local incrementValue = tonumber(ARGV[1]) " +
            "if redis.call('EXISTS', key) == 1 then " +
            "    return redis.call('INCRBY', key, incrementValue) " +
            "else " +
            "    return incrementValue " +
            "end";

    public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    
    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) {
        // 执行 Lua 脚本
        RedisScript<Long> script = new DefaultRedisScript<>(LUA_SCRIPT, Long.class);
        // 执行 Lua 脚本
        return stringRedisTemplate.execute(
                script,
                Collections.singletonList(key),
                String.valueOf(val)
        );
    }

    @Override
    public void setExpire(String key, long l) {
        stringRedisTemplate.expire(key, l, TimeUnit.SECONDS);
    }
}

动态实现类

resources/META-INF/services/com.anji.captcha.service.CaptchaCacheService

内容:com.captcha.service.CaptchaCacheServiceRedisImpl --实现类的包路径

Java SPI 机制概述 Java SPI

机制允许开发者为某些接口提供实现,而不需要直接修改应用程序的源代码。通过这种方式,应用程序能够在运行时动态地加载接口的实现类。常见的使用场景包括数据库驱动、日志框架等。

获取验证码

java 复制代码
@RestController
@RequiredArgsConstructor
@RequestMapping("/captcha")
public class CaptchaController {


    private final CaptchaService captchaService;

    @PostMapping("/getCaptcha")
    public R get(@RequestBody CaptchaVO data) {
        return R.success("获取成功",captchaService.get(data));
    }

    //@PostMapping("/check")
   // public ResponseModel check(@RequestBody CaptchaVO data) {
    //  return captchaService.check(data);
   // }


}

响应参数

java 复制代码
{
    "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
}

请求参数

java 复制代码
{
	"captchaType": "blockPuzzle",  //验证码类型 clickWord
	"clientUid": "唯一标识"  //客户端UI组件id,组件初始化时设置一次,UUID(非必传参数)
}

缓存信息

后端验证

java 复制代码
@PostMapping("/login")
public ResponseModel get(@RequestParam("captchaVerification") String captchaVerification) {
    CaptchaVO captchaVO = new CaptchaVO();
    captchaVO.setCaptchaVerification(captchaVerification);
    ResponseModel response = captchaService.verification(captchaVO);
    if(response.isSuccess() == false){
        //验证码校验失败,返回信息告诉前端
        //repCode  0000  无异常,代表成功
        //repCode  9999  服务器内部异常
        //repCode  0011  参数不能为空
        //repCode  6110  验证码已失效,请重新获取
        //repCode  6111  验证失败
        //repCode  6112  获取验证码失败,请联系管理员
    }
    return response;
}

前端请求

java 复制代码
{
	 "captchaType": "blockPuzzle",
	 "pointJson": "QxIVdlJoWUi04iM+65hTow==",  //aes加密坐标信息
	 "token": "71dd26999e314f9abb0c635336976635"  //get请求返回的token
}

自定义验证码

配置文件开启

yml 复制代码
aj:
  captcha:
    # 滑动验证底图路径,不配置将使用默认图片
    jigsaw: classpath:images/jigsaw

路径格式

images/jigsaw

  • original 背景图

  • slidingBlock 验证码块

相关推荐
计算机学姐1 分钟前
基于SpringBoot的在线教育管理系统
java·vue.js·spring boot·后端·mysql·spring·mybatis
onkel in blog2 分钟前
【Docker】Docker Compose方式搭建分布式内存数据库(Redis)集群
数据库·redis·分布式·docker
菜鸟破茧计划4 分钟前
滑动窗口:穿越数据的时光机
java·数据结构·算法
windwant14 分钟前
深入解析Http11AprProtocol:Tomcat高性能通信的底层原理
java·tomcat
Minyy1115 分钟前
“爱生活”小项目问题总结
java·数据库·spring boot·spring·maven·intellij-idea
Cloud Traveler26 分钟前
Java并发编程常见问题与陷阱解析
java·开发语言·python
Learning_foolish37 分钟前
ThreadLocal
java
有梦想的攻城狮1 小时前
spring中的@Value注解详解
java·后端·spring·value注解
少了一只鹅1 小时前
深入理解指针(5)
java·c语言·数据结构·算法
好吃的肘子2 小时前
ElasticSearch入门详解
java·大数据·elasticsearch·搜索引擎·云原生