引言
随着互联网的快速发展,各种在线服务和应用变得越来越普及,同时也带来了大量的自动化攻击和滥用行为。为了保护网站和服务免受恶意机器人的侵扰,验证码(Captcha)技术应运而生。AJ-Captcha 是一种高效、安全且易于集成的验证码解决方案,本文将详细介绍AJ-Captcha 的技术原理、实现方式及其应用场景。
应用场景
- 注册登录保护:在用户注册账号或登录已有账户时启用AJ-Captcha,可以有效阻止恶意注册和暴力破解尝试。
- 表单提交防护:对于那些容易受到大量垃圾信息骚扰的在线表格(如反馈建议、活动报名等),添加AJ-Captcha能显著降低无效数据量。
- 支付确认环节:在涉及资金交易的关键步骤加入额外的身份验证措施,确保每笔订单都出自合法用户之手。
- 资源下载限制:对于一些珍贵资料或者受版权保护的内容,可以通过设置AJ-Captcha作为下载前的最后一道防线,防止未经授权的批量抓取。
具体实现
1.依赖引入
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- captcha 图形验证码-->
<dependency>
<groupId>com.anji-plus</groupId>
<artifactId>spring-boot-starter-captcha</artifactId>
<version>1.3.0</version>
</dependency>
2.配置yml
server:
port: 10001
# 数据库配置
spring:
datasource:
driver-class-name: com.kingbase8.Driver
url: jdbc:kingbase8://localhost:54321/test?currentSchema=public
username: system
password: root
redis:
host: localhost
password: yianweilai
port: 6379
# Anji-plus 验证码配置
aj:
captcha:
# 缓存类型
cache-type: redis
# blockPuzzle 滑块 clickWord 文字点选 default默认两者都实例化
type: clickWord
# 校验滑动拼图允许误差偏移量(默认5像素)
slip-offset: 5
# aes加密坐标开启或者禁用(true|false)
aes-status: true
# 滑动干扰项(0/1/2)
interference-options: 0
# 右下角水印
water-mark: "一安未来"
3.配置Redis和Captcha
@Configuration
public class RedisConfig {
@Resource
private RedisConnectionFactory factory;
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
@Service
public class CaptchaCacheServiceRedis implements CaptchaCacheService {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public String type() {
return "redis";
}
@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);
}
}
4.加载服务实现
在 resources 目录下新建 META-INF/services,在 services 目录下新建 com.anji.captcha.service.CaptchaCacheService 的文件,内容指向 CaptchaCacheService 的配置路径。
5.编写首页
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>验证码示例</title>
<link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="/css/verify.css">
<script src="/js/jquery.min.js"></script>
<script src="/js/crypto-js.js"></script>
<script src="/js/ase.js"></script>
<script src="/js/verify.js"></script>
<style>
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
}
.container {
margin-top: -80px;
width: 78%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 10px;
border: 1px solid gray;
padding: 25px;
}
form {
width: 80%;
}
.btn {
border: none;
outline: none;
width: 300px;
height: 40px;
line-height: 40px;
text-align: center;
cursor: pointer;
background-color: #409EFF;
color: #fff;
font-size: 16px;
letter-spacing: 1em;
}
</style>
</head>
<body>
<div class="container">
<h2>请登录</h2>
<form th:action="@{/login}" method="post">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" class="form-control" id="username" name="username" />
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" class="form-control" id="password" name="password" />
</div>
<div class="form-group">
<button class="btn btn-primary" id='btn' type="button" >登录</button>
<div id="mpanel" style="margin-top:50px;"></div>
<div id="tip" style="margin-top:50px;"></div>
</div>
</form>
</div>
<script>
//slideVerify 对应blockPuzzle模式
//pointsVerify 对应clickWord模式
$('#mpanel').pointsVerify({
baseUrl: 'http://localhost:10001',
mode: 'pop',
containerId: 'btn',//pop模式 必填 被点击之后出现行为验证码的元素id
imgSize: {
width: '400px',
height: '200px'
},
barSize: {
width: '400px',
height: '40px'
},
beforeCheck: function () {
var name = $("#username").val();
var pass = $('#password').val();
if (name === '' || pass === '') {
$("#tip").html('<div class="alert alert-danger">请输入用户名和密码!</div>');
setTimeout(function() {
$("#tip div.alert").fadeOut(500);
}, 5000);
return false;
}
return true;
},
ready: function () {},
success: function (params) {
var name = $("#username").val();
var pass = $('#password').val();
$.ajax({
type: "POST",
url: "/login",
data: {
username: name,
password: pass
},
success: function (response) {
if (response.success) {
$("#tip").html('<div class="alert alert-success">登录成功</div>');
} else {
$("#tip").html('<div class="alert alert-danger">登录失败</div>');
}
setTimeout(function() {
$("#tip div.alert").fadeOut(500);
}, 5000);
},
error: function () {
$("#tip").html('<div class="alert alert-danger">登录请求出错</div>');
setTimeout(function() {
$("#tip div.alert").fadeOut(500);
}, 5000);
}
});
},
error: function () {}
});
</script>
</body>
</html>