介绍
滑块验证码比传统的字符验证码更加直观和用户友好,能够很好防止爬虫获取数据。
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
效果图
依赖
<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配置
spring:
redis:
host: 127.0.0.1
port: 6379
password:
database: 1
timeout: 6000
验证码配置
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
配置类
@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;
// }
}
实现类
/**
* 对于分布式部署的应用,我们建议应用自己实现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
机制允许开发者为某些接口提供实现,而不需要直接修改应用程序的源代码。通过这种方式,应用程序能够在运行时动态地加载接口的实现类。常见的使用场景包括数据库驱动、日志框架等。
获取验证码
@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);
// }
}
响应参数
{
"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
}
请求参数
{
"captchaType": "blockPuzzle", //验证码类型 clickWord
"clientUid": "唯一标识" //客户端UI组件id,组件初始化时设置一次,UUID(非必传参数)
}
缓存信息
后端验证
@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;
}
前端请求
{
"captchaType": "blockPuzzle",
"pointJson": "QxIVdlJoWUi04iM+65hTow==", //aes加密坐标信息
"token": "71dd26999e314f9abb0c635336976635" //get请求返回的token
}
自定义验证码
配置文件开启
aj:
captcha:
# 滑动验证底图路径,不配置将使用默认图片
jigsaw: classpath:images/jigsaw
路径格式
images/jigsaw
- original 背景图
- slidingBlock 验证码块