Spring Boot integrates AJ-Captcha to implement sliding verification code function
1.AJ-Captcha introduction
behavior vertification code
Adopting embedded integration method, access is convenient, safe and efficient. Abandoning the traditional character-type verification code display — filling in characters — comparing answers, the verification code display — collecting user behavior — analyzing user behavior process is adopted. Users only need to generate specified behavior trajectories without manual keyboard input, which is greatly optimized. It solves the problem of poor user experience with traditional verification codes; at the same time, it quickly and accurately returns human-machine judgment results.
AJ-Captcha behavioral vertification code
AJ-Captcha behavioral verification code includes two methods: sliding puzzle and text click. The UI supports two methods: pop-up and embedding. The backend provides Java implementation, and the frontend provides code examples such as php, angular, html, vue, uni-app, flutter, android, ios, etc.。
2.Code Project
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot-demo</artifactId>
<groupId>com.et</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Captcha</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.anji-plus</groupId>
<artifactId>spring-boot-starter-captcha</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
</project>
application.yaml
spring.application.name=captcha-service
server.port=8080
# 滑动验证,底图路径,不配置将使用默认图片
# 支持全路径
# 支持项目路径,以classpath:开头,取resource目录下路径,例:classpath:images/jigsaw
aj.captcha.jigsaw=classpath:images/jigsaw
# 滑动验证,底图路径,不配置将使用默认图片
# 支持全路径
# 支持项目路径,以classpath:开头,取resource目录下路径,例:classpath:images/pic-click
aj.captcha.pic-click=classpath:images/pic-click
# 对于分布式部署的应用,我们建议应用自己实现CaptchaCacheService,比如用Redis或者memcache,
# 参考CaptchaCacheServiceRedisImpl.java
# 如果应用是单点的,也没有使用redis,那默认使用内存。
# 内存缓存只适合单节点部署的应用,否则验证码生产与验证在节点之间信息不同步,导致失败。
# !!! 注意啦,如果应用有使用spring-boot-starter-data-redis,
# 请打开CaptchaCacheServiceRedisImpl.java注释。
# redis -----> SPI: 在resources目录新建META-INF.services文件夹(两层),参考当前服务resources。
# 缓存local/redis...
aj.captcha.cache-type=local
# local缓存的阈值,达到这个值,清除缓存
#aj.captcha.cache-number=1000
# local定时清除过期缓存(单位秒),设置为0代表不执行
#aj.captcha.timing-clear=180
#spring.redis.host=10.108.11.46
#spring.redis.port=6379
#spring.redis.password=
#spring.redis.database=2
#spring.redis.timeout=6000
# 验证码类型default两种都实例化。
aj.captcha.type=default
# 汉字统一使用Unicode,保证程序通过@value读取到是中文,可通过这个在线转换
# https://tool.chinaz.com/tools/unicode.aspx 中文转Unicode
# 右下角水印文字(我的水印)
aj.captcha.water-mark=我的水印
# 右下角水印字体(不配置时,默认使用文泉驿正黑)
# 由于宋体等涉及到版权,我们jar中内置了开源字体【文泉驿正黑】
# 方式一:直接配置OS层的现有的字体名称,比如:宋体
# 方式二:自定义特定字体,请将字体放到工程resources下fonts文件夹,支持ttf\ttc\otf字体
# aj.captcha.water-font=WenQuanZhengHei.ttf
# 点选文字验证码的文字字体(文泉驿正黑)
# aj.captcha.font-type=WenQuanZhengHei.ttf
# 校验滑动拼图允许误差偏移量(默认5像素)
aj.captcha.slip-offset=5
# aes加密坐标开启或者禁用(true|false)
aj.captcha.aes-status=true
# 滑动干扰项(0/1/2)
aj.captcha.interference-options=2
#点选字体样式 默认Font.BOLD
aj.captcha.font-style=1
#点选字体字体大小
aj.captcha.font-size=25
#点选文字个数,存在问题,暂不支持修改
#aj.captcha.click-word-count=4
aj.captcha.history-data-clear-enable=false
# 接口请求次数一分钟限制是否开启 true|false
aj.captcha.req-frequency-limit-enable=false
# 验证失败5次,get接口锁定
aj.captcha.req-get-lock-limit=5
# 验证失败后,锁定时间间隔,s
aj.captcha.req-get-lock-seconds=360
# get接口一分钟内请求数限制
aj.captcha.req-get-minute-limit=30
# check接口一分钟内请求数限制
aj.captcha.req-check-minute-limit=30
# verify接口一分钟内请求数限制(暂用不上,可后台直接调用captchaService)
#aj.captcha.req-verify-minute-limit=30
CaptchaConfig.java
package com.et.captcha.config;
import com.et.captcha.service.CaptchaCacheServiceRedisImpl;
import com.anji.captcha.properties.AjCaptchaProperties;
import com.anji.captcha.service.CaptchaCacheService;
import com.anji.captcha.service.impl.CaptchaServiceFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.core.StringRedisTemplate;
@Configuration
public class CaptchaConfig {
@Autowired
StringRedisTemplate redisTemplate;
@Bean(name = "CaptchaCacheService")
@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;
}
}
CaptchaCacheService has many implementation classes and can be dynamically expanded. For specific methods, please refer to the official tutorial. The default local implementation DefaultCaptchaServiceImpl is proposed below.
package com.anji.captcha.service.impl;
import com.anji.captcha.model.common.RepCodeEnum;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import com.anji.captcha.util.StringUtils;
import java.util.Iterator;
import java.util.Properties;
public class DefaultCaptchaServiceImpl extends AbstractCaptchaService {
public DefaultCaptchaServiceImpl() {
}
public String captchaType() {
return "default";
}
public void init(Properties config) {
Iterator var2 = CaptchaServiceFactory.instances.keySet().iterator();
while(var2.hasNext()) {
String s = (String)var2.next();
if (!this.captchaType().equals(s)) {
this.getService(s).init(config);
}
}
}
public void destroy(Properties config) {
Iterator var2 = CaptchaServiceFactory.instances.keySet().iterator();
while(var2.hasNext()) {
String s = (String)var2.next();
if (!this.captchaType().equals(s)) {
this.getService(s).destroy(config);
}
}
}
private CaptchaService getService(String captchaType) {
return (CaptchaService)CaptchaServiceFactory.instances.get(captchaType);
}
public ResponseModel get(CaptchaVO captchaVO) {
if (captchaVO == null) {
return RepCodeEnum.NULL_ERROR.parseError(new Object[]{"captchaVO"});
} else {
return StringUtils.isEmpty(captchaVO.getCaptchaType()) ? RepCodeEnum.NULL_ERROR.parseError(new Object[]{"类型"}) : this.getService(captchaVO.getCaptchaType()).get(captchaVO);
}
}
public ResponseModel check(CaptchaVO captchaVO) {
if (captchaVO == null) {
return RepCodeEnum.NULL_ERROR.parseError(new Object[]{"captchaVO"});
} else if (StringUtils.isEmpty(captchaVO.getCaptchaType())) {
return RepCodeEnum.NULL_ERROR.parseError(new Object[]{"类型"});
} else {
return StringUtils.isEmpty(captchaVO.getToken()) ? RepCodeEnum.NULL_ERROR.parseError(new Object[]{"token"}) : this.getService(captchaVO.getCaptchaType()).check(captchaVO);
}
}
public ResponseModel verification(CaptchaVO captchaVO) {
if (captchaVO == null) {
return RepCodeEnum.NULL_ERROR.parseError(new Object[]{"captchaVO"});
} else if (StringUtils.isEmpty(captchaVO.getCaptchaVerification())) {
return RepCodeEnum.NULL_ERROR.parseError(new Object[]{"二次校验参数"});
} else {
try {
String codeKey = String.format(REDIS_SECOND_CAPTCHA_KEY, captchaVO.getCaptchaVerification());
if (!CaptchaServiceFactory.getCache(cacheType).exists(codeKey)) {
return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_INVALID);
}
CaptchaServiceFactory.getCache(cacheType).delete(codeKey);
} catch (Exception var3) {
this.logger.error("验证码坐标解析失败", var3);
return ResponseModel.errorMsg(var3.getMessage());
}
return ResponseModel.success();
}
}
}
get verifucation Code
package com.anji.captcha.controller;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import com.anji.captcha.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping({"/captcha"})
public class CaptchaController {
@Autowired
private CaptchaService captchaService;
public CaptchaController() {
}
@PostMapping({"/get"})
public ResponseModel get(@RequestBody CaptchaVO data, HttpServletRequest request) {
assert request.getRemoteHost() != null;
data.setBrowserInfo(getRemoteId(request));
return this.captchaService.get(data);
}
@PostMapping({"/check"})
public ResponseModel check(@RequestBody CaptchaVO data, HttpServletRequest request) {
data.setBrowserInfo(getRemoteId(request));
return this.captchaService.check(data);
}
public ResponseModel verify(@RequestBody CaptchaVO data, HttpServletRequest request) {
return this.captchaService.verification(data);
}
public static final String getRemoteId(HttpServletRequest request) {
String xfwd = request.getHeader("X-Forwarded-For");
String ip = getRemoteIpFromXfwd(xfwd);
String ua = request.getHeader("user-agent");
return StringUtils.isNotBlank(ip) ? ip + ua : request.getRemoteAddr() + ua;
}
private static String getRemoteIpFromXfwd(String xfwd) {
if (StringUtils.isNotBlank(xfwd)) {
String[] ipList = xfwd.split(",");
return StringUtils.trim(ipList[0]);
} else {
return null;
}
}
}
verify verification Code
package com.et.captcha.controller;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 后端二次校验接口示例
*/
@RestController
@RequestMapping("/auth")
public class LoginController {
@Autowired
private CaptchaService captchaService;
@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;
}
}
There are too many front-end engineering codes so I won’t post them. For details, you can check the content in the web-ui file. The above are just some key codes. For all codes, please see the code repository below.
Code Repository
3.Test
Step one: Start the backend and start the StartApplication of service/springboot.
Step two: Start the frontend,enter into directory( view/vue) by terminal,input below command
yarn install
yarn run dev
Browser login http://localhost:8081/#/