本文目录导读:

我来详细讲解Java实现IP封禁的几种常见方案和案例。
基于内存的简易IP封禁
基础实现示例
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class SimpleIpBlocker {
// IP访问次数记录
private static ConcurrentHashMap<String, AtomicInteger> accessCount = new ConcurrentHashMap<>();
// 封禁的IP集合
private static ConcurrentHashMap<String, Long> blockedIps = new ConcurrentHashMap<>();
// 配置参数
private static final int MAX_ATTEMPTS = 10; // 最大尝试次数
private static final long BLOCK_DURATION = 60000; // 封禁时长(毫秒)
/**
* 检查IP是否被封禁
*/
public static boolean isBlocked(String ip) {
// 检查是否在封禁列表中
Long blockTime = blockedIps.get(ip);
if (blockTime != null) {
// 检查封禁是否过期
if (System.currentTimeMillis() - blockTime > BLOCK_DURATION) {
// 封禁过期,解除封禁
blockedIps.remove(ip);
accessCount.remove(ip);
return false;
}
return true;
}
return false;
}
/**
* 记录IP访问
*/
public static void recordAccess(String ip) {
if (isBlocked(ip)) {
return;
}
// 原子递增访问计数
AtomicInteger counter = accessCount.computeIfAbsent(ip, k -> new AtomicInteger(0));
int count = counter.incrementAndGet();
// 超过阈值则封禁
if (count >= MAX_ATTEMPTS) {
blockedIps.put(ip, System.currentTimeMillis());
System.out.println("IP " + ip + " 已被封禁,访问次数: " + count);
}
}
/**
* 手动解封IP
*/
public static void unblockIp(String ip) {
blockedIps.remove(ip);
accessCount.remove(ip);
System.out.println("IP " + ip + " 已被解封");
}
}
过滤器实现
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class IpBlockFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化配置
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 获取客户端IP
String clientIp = getClientIp(httpRequest);
// 检查IP是否被封禁
if (SimpleIpBlocker.isBlocked(clientIp)) {
httpResponse.setStatus(403);
httpResponse.getWriter().write("您的IP已被封禁");
return;
}
// 记录访问
SimpleIpBlocker.recordAccess(clientIp);
// 继续执行后续操作
chain.doFilter(request, response);
}
/**
* 获取真实客户端IP
*/
private String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
@Override
public void destroy() {
// 清理资源
}
}
基于Redis的分布式IP封禁
Redis实现方案
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Response;
public class RedisIpBlocker {
private JedisPool jedisPool;
// 缓存配置
private static final String BLOCK_PREFIX = "ip:block:";
private static final String COUNT_PREFIX = "ip:count:";
private static final int MAX_ATTEMPTS = 10;
private static final int BLOCK_DURATION = 600; // 秒
private static final int COUNT_WINDOW = 60; // 统计窗口(秒)
public RedisIpBlocker(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
/**
* 检查IP是否被封禁
*/
public boolean isBlocked(String ip) {
try (Jedis jedis = jedisPool.getResource()) {
String blockKey = BLOCK_PREFIX + ip;
return jedis.exists(blockKey);
}
}
/**
* 记录访问并检查是否需要封禁
*/
public boolean checkAndRecord(String ip) {
try (Jedis jedis = jedisPool.getResource()) {
String countKey = COUNT_PREFIX + ip;
// 使用Lua脚本保证原子性
String luaScript =
"local count = redis.call('incr', KEYS[1])\n" +
"redis.call('expire', KEYS[1], ARGV[1])\n" +
"if count >= tonumber(ARGV[2]) then\n" +
" redis.call('setex', KEYS[2], ARGV[3], '1')\n" +
" return 1\n" +
"end\n" +
"return 0";
Object result = jedis.eval(luaScript, 2,
countKey,
BLOCK_PREFIX + ip,
String.valueOf(COUNT_WINDOW),
String.valueOf(MAX_ATTEMPTS),
String.valueOf(BLOCK_DURATION));
return "1".equals(result.toString());
}
}
/**
* 解封IP
*/
public void unblockIp(String ip) {
try (Jedis jedis = jedisPool.getResource()) {
Pipeline pipeline = jedis.pipelined();
pipeline.del(BLOCK_PREFIX + ip);
pipeline.del(COUNT_PREFIX + ip);
pipeline.sync();
}
}
/**
* 获取IP访问次数
*/
public long getAccessCount(String ip) {
try (Jedis jedis = jedisPool.getResource()) {
String count = jedis.get(COUNT_PREFIX + ip);
return count != null ? Long.parseLong(count) : 0;
}
}
/**
* 获取被封禁的IP列表
*/
public Set<String> getBlockedIps() {
try (Jedis jedis = jedisPool.getResource()) {
return jedis.keys(BLOCK_PREFIX + "*");
}
}
}
Spring Boot集成Redis配置
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, String> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new StringRedisSerializer());
return template;
}
@Bean
public RedisScript<Long> ipBlockScript() {
String script =
"local count = redis.call('incr', KEYS[1])\n" +
"redis.call('expire', KEYS[1], ARGV[1])\n" +
"if count >= tonumber(ARGV[2]) then\n" +
" redis.call('setex', KEYS[2], ARGV[3], '1')\n" +
" return 1\n" +
"end\n" +
"return 0";
return new DefaultRedisScript<>(script, Long.class);
}
}
完整的Spring Boot应用示例
配置类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private IpBlockInterceptor ipBlockInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(ipBlockInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns("/api/health", "/api/unblock");
}
}
拦截器实现
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class IpBlockInterceptor implements HandlerInterceptor {
@Autowired
private RedisIpBlocker redisIpBlocker;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String clientIp = getClientIp(request);
// 检查是否被封禁
if (redisIpBlocker.isBlocked(clientIp)) {
response.setStatus(403);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":403,\"message\":\"IP已被封禁\"}");
return false;
}
// 记录访问并检查
if (redisIpBlocker.checkAndRecord(clientIp)) {
response.setStatus(403);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":403,\"message\":\"访问频繁,IP已被封禁\"}");
return false;
}
return true;
}
private String getClientIp(HttpServletRequest request) {
// 获取真实IP的代码同上
// ...
}
}
封禁管理接口
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/ip-block")
public class IpBlockController {
@Autowired
private RedisIpBlocker redisIpBlocker;
/**
* 手动封禁IP
*/
@PostMapping("/block")
public String blockIp(@RequestParam String ip,
@RequestParam(required = false, defaultValue = "3600") long duration) {
// 实现手动封禁
return "IP已封禁";
}
/**
* 解封IP
*/
@PostMapping("/unblock")
public String unblockIp(@RequestParam String ip) {
redisIpBlocker.unblockIp(ip);
return "IP已解封";
}
/**
* 查询IP状态
*/
@GetMapping("/status")
public Map<String, Object> getIpStatus(@RequestParam String ip) {
Map<String, Object> result = new HashMap<>();
result.put("ip", ip);
result.put("blocked", redisIpBlocker.isBlocked(ip));
result.put("accessCount", redisIpBlocker.getAccessCount(ip));
return result;
}
}
高级封禁策略
滑动窗口算法
public class SlidingWindowBlocker {
private static final int WINDOW_SIZE = 60; // 窗口大小(秒)
private static final int MAX_REQUESTS = 100; // 最大请求数
private final RedisTemplate<String, String> redisTemplate;
public boolean checkSlidingWindow(String ip) {
String key = "ip:sliding:" + ip;
long now = System.currentTimeMillis() / 1000;
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(
"redis.call('zremrangebyscore', KEYS[1], 0, ARGV[1] - 3600)\n" +
"local count = redis.call('zcard', KEYS[1])\n" +
"if count < tonumber(ARGV[2]) then\n" +
" redis.call('zadd', KEYS[1], ARGV[1], ARGV[1])\n" +
" redis.call('expire', KEYS[1], ARGV[3])\n" +
" return 0\n" +
"end\n" +
"return 1"
);
script.setResultType(Long.class);
List<String> keys = Arrays.asList(key);
Long result = redisTemplate.execute(script, keys,
String.valueOf(now),
String.valueOf(MAX_REQUESTS),
String.valueOf(WINDOW_SIZE));
return result == 1;
}
}
动态阈值调整
public class DynamicThresholdBlocker {
// 动态调整访问阈值
public int getThreshold(String ip) {
// 根据IP的访问历史动态调整
String historyKey = "ip:history:" + ip;
// 获取过去24小时的访问次数
String count = redisTemplate.opsForValue().get(historyKey);
long totalCount = count != null ? Long.parseLong(count) : 0;
// 根据访问频率动态调整阈值
if (totalCount > 10000) {
return 5; // 高频IP,降低阈值
} else if (totalCount > 1000) {
return 10;
} else {
return 20; // 正常IP,使用默认阈值
}
}
}
使用建议
部署配置
# application.yml
ip-block:
enabled: true
max-attempts: 10
block-duration: 600
count-window: 60
whitelist:
- 127.0.0.1
- 192.168.1.0/24
最佳实践
- 多层防护:结合Nginx、防火墙等多层防护
- 分段处理:不同接口使用不同的封禁策略
- 日志记录:记录所有封禁事件以便审计
- 白名单机制:对内网IP、健康检查等放行
- 自动解封:设置合理的自动解封时间
这个完整的Java IP封禁方案可以根据实际需求选择合适的实现方式。