1
This commit is contained in:
@@ -0,0 +1,160 @@
|
|||||||
|
package com.ruoyi.web.controller.public_;
|
||||||
|
|
||||||
|
import com.ruoyi.common.annotation.RateLimiter;
|
||||||
|
import com.ruoyi.common.constant.CacheConstants;
|
||||||
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
import com.ruoyi.common.enums.LimitType;
|
||||||
|
import com.ruoyi.jarvis.service.IInstructionService;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公开订单提交控制器
|
||||||
|
* 用于接收外部提交的订单信息
|
||||||
|
* 特点:
|
||||||
|
* 1. 无需登录认证
|
||||||
|
* 2. 带接口限流保护
|
||||||
|
* 3. 详细的日志记录
|
||||||
|
* 4. 只允许使用"单"指令提交订单
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/public/order")
|
||||||
|
public class PublicOrderController extends BaseController {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(PublicOrderController.class);
|
||||||
|
|
||||||
|
private final IInstructionService instructionService;
|
||||||
|
|
||||||
|
public PublicOrderController(IInstructionService instructionService) {
|
||||||
|
this.instructionService = instructionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交订单
|
||||||
|
*
|
||||||
|
* 限流策略:
|
||||||
|
* - 每个IP每分钟最多3次请求
|
||||||
|
* - 防止恶意刷单和攻击
|
||||||
|
*
|
||||||
|
* @param body 请求体,包含command字段
|
||||||
|
* @param request HTTP请求对象,用于获取客户端信息
|
||||||
|
* @return 执行结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/submit")
|
||||||
|
@RateLimiter(
|
||||||
|
key = CacheConstants.RATE_LIMIT_KEY,
|
||||||
|
time = 60,
|
||||||
|
count = 3,
|
||||||
|
limitType = LimitType.IP
|
||||||
|
)
|
||||||
|
public AjaxResult submit(@RequestBody Map<String, String> body, HttpServletRequest request) {
|
||||||
|
// 获取客户端信息用于日志记录
|
||||||
|
String clientIp = getClientIp(request);
|
||||||
|
String userAgent = request.getHeader("User-Agent");
|
||||||
|
|
||||||
|
// 获取指令内容
|
||||||
|
String cmd = body != null ? body.get("command") : null;
|
||||||
|
|
||||||
|
// 记录请求日志
|
||||||
|
log.info("======================================");
|
||||||
|
log.info("公开订单提交 - 开始");
|
||||||
|
log.info("客户端IP: {}", clientIp);
|
||||||
|
log.info("User-Agent: {}", userAgent);
|
||||||
|
log.info("请求时间: {}", new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date()));
|
||||||
|
|
||||||
|
// 参数校验
|
||||||
|
if (cmd == null || cmd.trim().isEmpty()) {
|
||||||
|
log.warn("参数校验失败: 指令内容为空");
|
||||||
|
log.info("公开订单提交 - 结束(失败)");
|
||||||
|
log.info("======================================");
|
||||||
|
return AjaxResult.error("请输入订单信息");
|
||||||
|
}
|
||||||
|
|
||||||
|
String trimmedCmd = cmd.trim();
|
||||||
|
log.info("指令内容长度: {} 字符", trimmedCmd.length());
|
||||||
|
log.info("指令内容预览: {}", trimmedCmd.length() > 100 ? trimmedCmd.substring(0, 100) + "..." : trimmedCmd);
|
||||||
|
|
||||||
|
// 安全检查:只允许"单"开头的指令
|
||||||
|
if (!trimmedCmd.startsWith("单:") && !trimmedCmd.startsWith("单:") && !trimmedCmd.startsWith("单")) {
|
||||||
|
log.warn("安全检查失败: 指令不是以'单'开头");
|
||||||
|
log.info("公开订单提交 - 结束(拒绝)");
|
||||||
|
log.info("======================================");
|
||||||
|
return AjaxResult.error("只允许提交订单信息,指令必须以'单:'开头");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行指令
|
||||||
|
List<String> result;
|
||||||
|
try {
|
||||||
|
log.info("开始执行订单指令...");
|
||||||
|
result = instructionService.execute(trimmedCmd);
|
||||||
|
log.info("订单指令执行完成");
|
||||||
|
|
||||||
|
// 记录执行结果
|
||||||
|
if (result != null && !result.isEmpty()) {
|
||||||
|
log.info("执行结果条数: {}", result.size());
|
||||||
|
for (int i = 0; i < result.size(); i++) {
|
||||||
|
String item = result.get(i);
|
||||||
|
if (item != null) {
|
||||||
|
// 检查是否包含警告标记
|
||||||
|
if (item.contains("[炸弹]")) {
|
||||||
|
log.warn("执行结果[{}]包含警告: {}", i, item);
|
||||||
|
} else if (item.contains("成功")) {
|
||||||
|
log.info("执行结果[{}]: 成功", i);
|
||||||
|
} else {
|
||||||
|
log.info("执行结果[{}]长度: {} 字符", i, item.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("执行结果为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("公开订单提交 - 结束(成功)");
|
||||||
|
log.info("======================================");
|
||||||
|
return AjaxResult.success(result);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("执行订单指令时发生异常", e);
|
||||||
|
log.error("异常类型: {}", e.getClass().getName());
|
||||||
|
log.error("异常消息: {}", e.getMessage());
|
||||||
|
log.info("公开订单提交 - 结束(异常)");
|
||||||
|
log.info("======================================");
|
||||||
|
return AjaxResult.error("订单提交失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取客户端真实IP地址
|
||||||
|
* 考虑代理和负载均衡的情况
|
||||||
|
*/
|
||||||
|
private String getClientIp(HttpServletRequest request) {
|
||||||
|
String ip = request.getHeader("X-Forwarded-For");
|
||||||
|
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("Proxy-Client-IP");
|
||||||
|
}
|
||||||
|
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||||
|
}
|
||||||
|
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("HTTP_CLIENT_IP");
|
||||||
|
}
|
||||||
|
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
|
||||||
|
}
|
||||||
|
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getRemoteAddr();
|
||||||
|
}
|
||||||
|
// 对于通过多个代理的情况,第一个IP为客户端真实IP
|
||||||
|
if (ip != null && ip.contains(",")) {
|
||||||
|
ip = ip.substring(0, ip.indexOf(",")).trim();
|
||||||
|
}
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -112,6 +112,8 @@ public class SecurityConfig
|
|||||||
permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
|
permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
|
||||||
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
|
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
|
||||||
requests.antMatchers("/login", "/register", "/captchaImage").permitAll()
|
requests.antMatchers("/login", "/register", "/captchaImage").permitAll()
|
||||||
|
// 公开接口,允许匿名访问
|
||||||
|
.antMatchers("/public/**").permitAll()
|
||||||
// 静态资源,可匿名访问
|
// 静态资源,可匿名访问
|
||||||
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
|
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
|
||||||
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
|
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
|
||||||
|
|||||||
@@ -413,11 +413,11 @@ public class InstructionServiceImpl implements IInstructionService {
|
|||||||
|
|
||||||
static {
|
static {
|
||||||
/*
|
/*
|
||||||
18539187615
|
|
||||||
15738558087
|
|
||||||
13243039070
|
13243039070
|
||||||
15639125541
|
15639125541
|
||||||
17530176250 */
|
17530176250
|
||||||
|
*/
|
||||||
|
|
||||||
phoneWithTF.add("13243039070");
|
phoneWithTF.add("13243039070");
|
||||||
phoneWithTF.add("15639125541");
|
phoneWithTF.add("15639125541");
|
||||||
|
|||||||
Reference in New Issue
Block a user