本文目录导读:

在Java中实现黑白名单功能,通常用于权限控制、IP访问限制、接口防刷、数据过滤等场景。
根据你的具体场景(是配置IP、用户ID、还是特定词条),配置方式会有所不同,以下是几个最常见的Java黑白名单配置案例及方案:
基于IP地址的黑白名单(后端最常用)
通常用于过滤恶意请求或限制内部访问,常用存储介质:配置文件(简单)或 Redis(动态)。
配置文件方式(适合规则少、不频繁变动的场景)
第一步:配置文件(application.yml 或 application.properties)
# application.properties # 黑名单IP,多个用逗号分隔 blacklist.ips=192.168.1.100,10.0.0.50 # 白名单IP(如果启用白名单,只有这些IP能访问) whitelist.ips=192.168.1.1,127.0.0.1 # 启用模式:black(黑名单模式,禁止黑名单访问) / white(白名单模式,仅允许白名单访问) access.mode=black
第二步:Java 配置加载类
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@ConfigurationProperties(prefix = "access")
public class AccessConfig {
private String mode;
private List<String> blacklistIps;
private List<String> whitelistIps;
// getter 和 setter 省略...
}
第二步:过滤器实现
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
@Component
public class IpFilter implements Filter {
private final AccessConfig accessConfig;
public IpFilter(AccessConfig accessConfig) {
this.accessConfig = accessConfig;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String ip = req.getRemoteAddr(); // 获取客户端IP
boolean isBlocked = false;
if ("black".equals(accessConfig.getMode())) {
// 黑名单模式:如果IP在黑名单里,禁止访问
isBlocked = accessConfig.getBlacklistIps().contains(ip);
} else if ("white".equals(accessConfig.getMode())) {
// 白名单模式:如果IP不在白名单里,禁止访问
isBlocked = !accessConfig.getWhitelistIps().contains(ip);
}
if (isBlocked) {
res.setStatus(403);
res.getWriter().write("Access Denied");
return;
}
chain.doFilter(request, response);
}
}
动态配置方式(适合规则频繁变化、需要热加载)
使用 Redis Set 结构,结合 AOP 或 拦截器。
import redis.clients.jedis.Jedis;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
public class IpWhitelistInterceptor implements HandlerInterceptor {
@Autowired
private StringRedisTemplate redisTemplate; // 假设引入了Spring Data Redis
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String ip = request.getRemoteAddr();
String whiteListKey = "ip:whitelist";
String blackListKey = "ip:blacklist";
// 1. 先检查黑名单(如果有)
Boolean isBlack = redisTemplate.opsForSet().isMember(blackListKey, ip);
if (Boolean.TRUE.equals(isBlack)) {
response.setStatus(403); // Access Forbidden
return false;
}
// 2. 检查白名单
Boolean isWhite = redisTemplate.opsForSet().isMember(whiteListKey, ip);
if (Boolean.TRUE.equals(isWhite)) {
return true; // 放行
}
// 3. 既不在黑名单也不在白名单,可以根据策略决定(允许/拒绝)
// 拒绝所有未在白名单的IP
response.setStatus(403);
return false;
}
}
优点: 运维可以直接通过 redis-cli sadd ip:whitelist 192.168.1.200 添加白名单,无需重启服务。
基于用户ID/角色的黑白名单(权限管理)
常用于封禁用户或VIP白名单。
配置方式(数据表):
设计一张数据库表 user_acl:
CREATE TABLE user_acl (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
type VARCHAR(10) NOT NULL COMMENT 'BLACK 或 WHITE',
reason VARCHAR(255),
created_at TIMESTAMP
);
Java 实现(以注解 + AOP 为例):
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckBlacklist {
String value() default "user";
}
@Aspect
@Component
public class BlacklistAspect {
@Autowired
private UserAclService userAclService;
@Around("@annotation(checkBlacklist)")
public Object around(ProceedingJoinPoint joinPoint, CheckBlacklist checkBlacklist) {
Long userId = getCurrentUser(); // 从Token或Session获取
boolean isBlack = userAclService.isUserBlacklisted(userId);
if (isBlack) {
throw new BusinessException("用户已被列入黑名单,无法操作");
}
return joinPoint.proceed();
}
}
使用:
@PostMapping("/transfer")
@CheckBlacklist // 需要检查黑名单
public Result transfer(@RequestBody TransferReq req) { ... }
基于敏感词的黑白名单(内容过滤)
常用于评论系统、用户昵称过滤。
配置方式(文件或数据库):
第一步:初始化敏感词库(启动时加载到内存)
@Component
public class SensitiveWordFilter {
// 黑名单敏感词
private Set<String> blackWords = new HashSet<>(Arrays.asList("垃圾", "广告", "骗子"));
// 白名单例外词(广告虽敏感,但“广告部”是正常词)
private Set<String> whiteWords = new HashSet<>(Arrays.asList("广告部", "广告公司"));
public boolean containsSensitiveWord(String text) {
for (String black : blackWords) {
if (text.contains(black)) {
// 检查是否命中白名单
boolean isException = whiteWords.stream().anyMatch(text::contains);
if (!isException) {
return true; // 检测到黑名单词且不在白名单例外中
}
}
}
return false;
}
}
注意: 实际项目中建议使用 Trie树(前缀树) 或 DFA算法 提高效率,上面仅为演示逻辑。
如何选择配置方案?
| 场景 | 推荐配置方式 | 特点 |
|---|---|---|
| API 访问控制(IP/API Key) | Redis + 拦截器 | 动态更新、高性能、分布式 |
| 权限白名单(用户/角色) | 数据库 + AOP | 持久化、可管理、适合复杂业务 |
| 敏感词过滤 | 本地缓存(如Caffeine) + 文件 | 低延迟、不依赖外部服务 |
| 简单业务测试 | application.yml + Filter | 快速开发、静态配置 |
总结建议
- 首选动态配置: 生产环境尽量避免修改代码重启,使用 Redis 或 数据库 存储规则。
- 双名单逻辑: 通常先判断黑名单(快速拒绝),再判断白名单(精确放行),最后根据默认策略(允许/拒绝)处理未匹配的请求。
- 注意性能: 如果黑白名单数据量很大(如百万级IP),不要使用
List.contains(),改用 HashSet 或 Redis Set 的 O(1) 查询。
你需要针对哪种具体的对象(IP、用户、关键词)做黑白名单?我可以给出更针对性的代码示例。