mysql的安装和使用
2026/10/28 1:17:18
✅ 滑块验证码 ✅ 行为验证码(无感 + 风控评分) ✅ Redis 状态 / Token / TTL ✅ 风险分级 → 决策 ✅ 可扩展点(模型 / 点选 / 短信)
真实项目结构
captcha-service ├── controller │ └── CaptchaController.java ├── service │ ├── SliderCaptchaService.java │ ├── BehaviorCaptchaService.java │ └── RiskDecisionService.java ├── model │ ├── SliderTrack.java │ ├── BehaviorEvent.java │ ├── CaptchaResult.java │ └── CaptchaDecision.java ├── util │ ├── TokenUtil.java │ └── RiskUtil.java ├── config │ └── RedisConfig.java └── CaptchaApplication.javapublic enum CaptchaDecision { PASS, SLIDER, REJECT }@Data @AllArgsConstructor public class CaptchaResult { private CaptchaDecision decision; private String captchaToken; }@Data public class SliderTrack { private int x; private int y; private long t; }@Data public class BehaviorEvent { private String type; // move / click / key private int x; private int y; private long timestamp; }public class TokenUtil { private static final String SECRET = "captcha-secret"; public static String generate(String sessionId) { String raw = sessionId + ":" + System.currentTimeMillis() + ":" + UUID.randomUUID(); return DigestUtils.sha256Hex(raw + SECRET); } }@Service public class SliderCaptchaService { public boolean verifyPosition(int userX, int targetX) { return Math.abs(userX - targetX) <= 5; } public boolean verifyTrack(List<SliderTrack> tracks) { if (tracks == null || tracks.size() < 10) return false; long duration = tracks.get(tracks.size() - 1).getT() - tracks.get(0).getT(); if (duration < 300) return false; boolean acc = false, dec = false; for (int i = 2; i < tracks.size(); i++) { int dx1 = tracks.get(i - 1).getX() - tracks.get(i - 2).getX(); int d