在Java Spring Boot项目中,防止接口盗刷(即防止恶意用户或脚本频繁调用接口)是一个重要的安全措施。以下是几种优雅的实现方式:

logo
1. 使用限流(Rate Limiting)限流是一种常见的防止接口盗刷的方法,可以限制某个时间段内对特定接口的请求次数。
使用Spring Cloud Gateway
如果你使用的是Spring Cloud Gateway,可以很方便地集成限流功能。

限流之Spring Cloud Gateway
@Beanpublic RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("limit_route", r -> r.path("/api/**") .filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(myRateLimiter()))) .uri("http://example.com")) .build();}@Beanpublic RateLimiter myRateLimiter() { return new RedisRateLimiter(10, 20); // 每秒最多10个请求,每分钟最多20个请求}使用Bucket4j
Bucket4j是一个基于令牌桶算法的Java限流库,可以很容易地集成到Spring Boot项目中。

令牌桶限流
@Configurationpublic RateLimitConfig { @Bean public Bucket createBucket() { Bandwidth limit = Bandwidth.classic(10, Refill.intervally(10, Duration.ofMinutes(1))); return Bucket4j.builder().addLimit(limit).build(); }}@RestControllerpublic MyController { private final Bucket bucket; public MyController(Bucket bucket) { this.bucket = bucket; } @GetMapping("/api/resource") public ResponseEntity<String> getResource() { if (bucket.tryConsume(1)) { return ResponseEntity.ok("Resource"); } return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("Too many requests"); }}2. 使用验证码对于一些敏感操作,可以要求用户输入验证码,以防止自动化脚本频繁调用接口。

验证码
使用Google reCAPTCHA
Google reCAPTCHA是一个广泛使用的验证码服务,可以有效防止自动化脚本。
前端代码如下:
<script src="https://www.google.com/recaptcha/api.js" async defer></script><div>在后端验证:@PostMapping("/submit")public ResponseEntity<String> submit(@RequestParam String recaptchaResponse) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap<String, String> map = new LinkedMultiValueMap<>(); map.add("secret", "your_secret_key"); map.add("response", recaptchaResponse); HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers); ResponseEntity<String> response = restTemplate.postForEntity("https://www.google.com/recaptcha/api/siteverify", request, String.class); if (response.getStatusCode() == HttpStatus.OK) { // 验证成功 } else { // 验证失败 } return response;}3. 使用JWT(JSON Web Token)JWT可以用于验证请求的合法性,防止未授权的请求。
生成和验证JWT
可以使用JJWT库来生成和验证JWT。

鉴权之JWT
@Configurationpublic JwtTokenUtil { private String secret = "your_secret_key"; public String generateToken(String username) { return Jwts.builder() .setSubject(username) .setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10小时 .signWith(SignatureAlgorithm.HS512, secret) .compact(); } public Boolean validateToken(String token, UserDetails userDetails) { final String username = getUsernameFromToken(token); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } private String getUsernameFromToken(String token) { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getSubject(); } private Boolean isTokenExpired(String token) { final Date expiration = getExpirationDateFromToken(token); return expiration.before(new Date()); } private Date getExpirationDateFromToken(String token) { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getExpiration(); }}4. 使用IP黑名单可以将恶意IP加入黑名单,禁止其访问接口。

代码实现IP黑名单
使用Spring Security
可以在Spring Security配置中添加IP黑名单。
@EnableWebSecuritypublic SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/api/**").access("not (hasIpAddress('192.168.1.1') or hasIpAddress('192.168.1.2'))") .anyRequest().authenticated(); }}总结以上几种方法可以单独使用,也可以组合使用,以提高接口的安全性。限流和验证码是比较通用的方法,而JWT和IP黑名单则更适合特定场景。根据具体需求选择合适的方法,可以有效防止接口盗刷。