介绍
一个基于Spring boot 2.4.2、JDK 1.8、Security、防恶意请求技术实现的前后端分离的脚手架,可以为开发人员省去前期框架调研和搭建的成本。
软件架构
- Spring boot 2.4.2
- JDK 1.8
- Security
- kk-anti-reptile(反爬虫接口防刷组件)
- Redis
- MySQL
- MyBatis
安装教程
- 克隆 本代码到本地
- 修改application.yml配置,包含Mysql数据库信息、Redis连接信息、kk-anti-reptile配置信息(不修改也可以,默认,详细参数见:kk-anti-reptile)
- 启动项目
- 使用测试工具或者前端连接请求
使用说明
本示例在TestWebSecurity
类中配置了Security前端登录接口和退出登录接口,如想自定义接口,请修改如下图圈中的配置
application.yml
配置文件中kk-anti-reptile
部分配置如下:
# 恶意请求
anti:
reptile:
manager:
# 激活恶意请求/反爬虫配置
enabled: true
# 需要拦截的请求,多个以“,”分隔,或者直接在controller方法上添加@AntiReptile
# include-urls: ^/admin/.*$
ua-rule:
#允许linux系统访问
allowed-linux: true
# 全局拦截,默认为false,为false时,需要配置上面的include-urls
global-filter-mode: true
详细参数请参见kk-anti-reptile
请求示例
登录接口
请求链接
http://localhost:8099/auth/login
请求参数
username:admin
password:1234
注意,此处需要使用表单提交,postman中如下:
响应参数
登录成功返回参数如下:
{
"code": "00000",
"msg": "操作成功!",
"data": {
"token": "3aa72e4a-74b5-4542-9e56-e7f31ec5ce09",
"permissions": [],
"loginTime": 1611284216123
}
}
其中,token的值就是前端需要在每次请求中在请求头部传递到后端的参数
退出登录接口
请求链接
http://localhost:8099/auth/logout
请求参数
请求头部需要放置如下参数:
X-token:token的值
Postman中如下:
响应参数
{
"code": "00000",
"msg": "您已经退出登录!"
}
正常接口
正常业务逻辑中调用的接口传参
请求链接
http://localhost:8099/test/listOrientations
请求参数
header中必须带有X-token参数,其他业务参数请写在请求体中
Postman中如下:
正常响应参数(没有超过反爬虫预警)
{
"code": "00000",
"msg": "ddd"
}
被拦截的响应参数
当使用postman多次请求,请求超过阈值,将会返回验证码页面(如果前端JQ请求,请参照kk-anti-reptile处理方式正常显示验证码)
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>kk-anti-reptile验证</title>
<script>
function getXhr() {
var xhr = null;
try {
xhr = new XMLHttpRequest();
} catch (e) {
try {
xhr = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
}
return xhr;
}
function refresh() {
var xhr = getXhr();
var verifyId = document.getElementById("verifyId").value;
var baseUrl = document.getElementById("baseUrl").value;
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
var verifyObj = JSON.parse(xhr.responseText);
document.getElementById("verifyId").value = verifyObj.verifyId;
document.getElementById("verifyImg").src = verifyObj.verifyImgStr;
}
}
xhr.open("POST", baseUrl + "/kk-anti-reptile/refresh?verifyId="+verifyId, "true");
xhr.send();
}
function validate() {
var elements = document.getElementById("verifyFrom");
var formData = new FormData();
for(var i = 0; i < elements.length; i++) {
formData.append(elements[i].name, elements[i].value);
}
var baseUrl = document.getElementById("baseUrl").value;
var xhr = getXhr();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
var obj = JSON.parse(xhr.responseText);
if (obj.result == true) {
closeThisWindows();
} else {
alert("验证码填写错误")
}
}
}
xhr.open("POST", baseUrl + "/kk-anti-reptile/validate", "true");
xhr.send(formData);
}
function closeThisWindows() {
if (navigator.userAgent.indexOf("MSIE") > 0) {
if (navigator.userAgent.indexOf("MSIE 6.0") > 0) {
window.opener = null;
window.close();
} else {
window.open('', '_top');
window.top.close();
}
} else if (navigator.userAgent.indexOf("Firefox") > 0) {
window.location.href = 'about:blank';
} else if (navigator.userAgent.indexOf("AppleWebKit") > 0) {
window.location.href = 'about:blank';
window.close();
} else {
window.opener = null;
window.open('', '_self', '');
window.close();
}
}
</script>
<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>普通验证码</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
form {
width: 240px;
margin: 100px auto;
padding: 20px;
}
input[type="text"] {
margin: 10px 0;
padding: 0 4px;
width: 100%;
height: 32px;
border: 1px solid #c3c3c3;
border-radius: 4px;
}
input[type="button"] {
width: 100%;
height: 32px;
color: #fff;
background-color: #40a9ff;
border-color: #40a9ff;
border-radius: 4px;
outline: 0;
cursor: pointer;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12);
box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
border-style: none;
}
.img-wrapper {
display: flex;
align-items: center;
}
.img-wrapper img {
width: 130px;
height: 48px;
}
.img-wrapper a {
text-decoration: none;
color: #1890ff;
}
</style>
</head>
<body>
<form id="verifyFrom" method="post" action="">
<input type="hidden" id="baseUrl" name="baseUrl">
<input type="hidden" id="verifyId" name="verifyId" value="d4eee0b1-b416-4231-96d8-8af6f70ad2c2">
<input type="hidden" id="realRequestUri" name="realRequestUri" value="/test/listOrientations">
<span>操作频繁,请输入验证码</span>
<div class="img-wrapper">
<img id="verifyImg" src="data:image/jpeg;base64,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">
<a href="javascript:void(0);" onclick="refresh()">刷新</a>
</div>
<input type="text" id="result" name="result">
<br/>
<input type="button" value="确认" onclick="validate()">
</form>
</body>
</html>
赞赏我
- 如果您觉得这个不错,可以打赏下作者,您的赞同是作者不懈努力的源泉