本文目录导读:

实现Java白名单校验通常有几种常见方案,我为你整理了一个完整的案例,包含从简单到企业级的实现方式。
基础实现方案
使用Set集合(最简单)
import java.util.HashSet;
import java.util.Set;
public class SimpleWhitelistValidator {
private Set<String> whitelist;
public SimpleWhitelistValidator() {
// 初始化白名单
whitelist = new HashSet<>();
whitelist.add("192.168.1.1");
whitelist.add("192.168.1.100");
whitelist.add("10.0.0.1");
}
public boolean isValid(String ip) {
return whitelist.contains(ip);
}
// 使用示例
public static void main(String[] args) {
SimpleWhitelistValidator validator = new SimpleWhitelistValidator();
String testIp = "192.168.1.1";
if (validator.isValid(testIp)) {
System.out.println(testIp + " 在白名单中,允许访问");
} else {
System.out.println(testIp + " 不在白名单中,拒绝访问");
}
}
}
支持通配符匹配
import java.util.ArrayList;
import java.util.List;
public class PatternWhitelistValidator {
private List<String> whitelistPatterns;
public PatternWhitelistValidator() {
whitelistPatterns = new ArrayList<>();
// 支持通配符 * 和 ?
whitelistPatterns.add("192.168.1.*"); // 匹配所有192.168.1.x
whitelistPatterns.add("10.0.?.1"); // 匹配10.0.x.1
whitelistPatterns.add("172.16.0.1-100"); // 匹配172.16.0.1到172.16.0.100
}
public boolean isValid(String ip) {
for (String pattern : whitelistPatterns) {
if (matchPattern(ip, pattern)) {
return true;
}
}
return false;
}
private boolean matchPattern(String ip, String pattern) {
// 处理范围匹配
if (pattern.contains("-")) {
return matchRange(ip, pattern);
}
// 处理通配符匹配
String regex = pattern
.replace(".", "\\.")
.replace("*", "[0-9]{1,3}")
.replace("?", "[0-9]");
return ip.matches(regex);
}
private boolean matchRange(String ip, String pattern) {
String[] parts = pattern.split("-");
if (parts.length != 2) return false;
String baseIp = parts[0].substring(0, parts[0].lastIndexOf(".") + 1);
int start = Integer.parseInt(parts[0].substring(parts[0].lastIndexOf(".") + 1));
int end = Integer.parseInt(parts[1]);
String ipBase = ip.substring(0, ip.lastIndexOf(".") + 1);
if (!ipBase.equals(baseIp)) return false;
int ipLast = Integer.parseInt(ip.substring(ip.lastIndexOf(".") + 1));
return ipLast >= start && ipLast <= end;
}
}
企业级实现方案
使用注解+AOP实现
// 1. 自定义注解
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WhitelistCheck {
String[] value() default {};
String param() default "ip";
}
// 2. 白名单配置类
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "whitelist")
public class WhitelistConfig {
private List<String> ips = new ArrayList<>();
private List<String> patterns = new ArrayList<>();
// getters and setters
public List<String> getIps() { return ips; }
public void setIps(List<String> ips) { this.ips = ips; }
public List<String> getPatterns() { return patterns; }
public void setPatterns(List<String> patterns) { this.patterns = patterns; }
}
// 3. AOP切面实现
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
@Aspect
@Component
public class WhitelistAspect {
@Autowired
private WhitelistConfig whitelistConfig;
@Around("@annotation(whitelistCheck)")
public Object checkWhitelist(ProceedingJoinPoint joinPoint, WhitelistCheck whitelistCheck) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
String clientIp = getClientIp(request);
// 获取方法参数中的IP(如果存在)
String paramIp = getParamIp(joinPoint, whitelistCheck.param());
String ipToCheck = paramIp != null ? paramIp : clientIp;
// 检查白名单
if (!isWhitelisted(ipToCheck, whitelistCheck.value())) {
throw new SecurityException("IP " + ipToCheck + " 不在白名单中");
}
return joinPoint.proceed();
}
private boolean isWhitelisted(String ip, String[] annotationWhitelist) {
// 先检查注解中的白名单
if (annotationWhitelist.length > 0) {
for (String allowed : annotationWhitelist) {
if (allowed.equals(ip)) return true;
}
}
// 再检查配置文件中的白名单
if (whitelistConfig.getIps().contains(ip)) return true;
// 检查模式匹配
for (String pattern : whitelistConfig.getPatterns()) {
if (ip.matches(patternToRegex(pattern))) return true;
}
return false;
}
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.getRemoteAddr();
}
return ip;
}
private String getParamIp(ProceedingJoinPoint joinPoint, String paramName) {
Object[] args = joinPoint.getArgs();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String[] paramNames = signature.getParameterNames();
for (int i = 0; i < paramNames.length; i++) {
if (paramNames[i].equals(paramName)) {
return (String) args[i];
}
}
return null;
}
private String patternToRegex(String pattern) {
return "^" + pattern
.replace(".", "\\.")
.replace("*", "[0-9]{1,3}")
.replace("?", "[0-9]") + "$";
}
}
// 4. 使用示例
@RestController
public class ApiController {
@GetMapping("/api/sensitive")
@WhitelistCheck(value = {"192.168.1.1"}, param = "ip")
public String sensitiveApi(@RequestParam(required = false) String ip) {
return "敏感数据";
}
@GetMapping("/api/admin")
@WhitelistCheck
public String adminApi() {
return "管理接口";
}
}
基于数据库的动态白名单
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import javax.persistence.*;
// 1. 实体类
@Entity
@Table(name = "whitelist")
public class WhitelistEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String ip;
@Column(name = "ip_type")
private String type; // SINGLE, RANGE, PATTERN
@Column(name = "ip_range_start")
private String rangeStart;
@Column(name = "ip_range_end")
private String rangeEnd;
private String description;
@Column(name = "is_active")
private Boolean active = true;
// getters and setters...
}
// 2. 仓库接口
public interface WhitelistRepository extends JpaRepository<WhitelistEntity, Long> {
@Query("SELECT w FROM WhitelistEntity w WHERE w.active = true AND w.ip = ?1")
List<WhitelistEntity> findByIp(String ip);
@Query("SELECT w FROM WhitelistEntity w WHERE w.active = true AND w.type = ?1")
List<WhitelistEntity> findByType(String type);
}
// 3. 缓存的服务类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class DynamicWhitelistService {
@Autowired
private WhitelistRepository repository;
@Cacheable(value = "whitelist", key = "#ip")
public boolean checkIp(String ip) {
// 1. 精确匹配
List<WhitelistEntity> exactMatches = repository.findByIp(ip);
if (!exactMatches.isEmpty()) return true;
// 2. 范围匹配
List<WhitelistEntity> rangeRules = repository.findByType("RANGE");
for (WhitelistEntity rule : rangeRules) {
if (isInRange(ip, rule.getRangeStart(), rule.getRangeEnd())) {
return true;
}
}
// 3. 模式匹配
List<WhitelistEntity> patternRules = repository.findByType("PATTERN");
for (WhitelistEntity rule : patternRules) {
if (ip.matches(patternToRegex(rule.getIp()))) {
return true;
}
}
return false;
}
private boolean isInRange(String ip, String start, String end) {
long ipLong = ipToLong(ip);
return ipLong >= ipToLong(start) && ipLong <= ipToLong(end);
}
private long ipToLong(String ipAddress) {
String[] ipArray = ipAddress.split("\\.");
long result = 0;
for (int i = 0; i < ipArray.length; i++) {
int power = 3 - i;
result |= (Long.parseLong(ipArray[i]) << (power * 8));
}
return result;
}
}
性能优化方案
使用布隆过滤器(适用于海量白名单)
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import javax.annotation.PostConstruct;
import java.nio.charset.Charset;
public class BloomFilterWhitelist {
private BloomFilter<String> bloomFilter;
@PostConstruct
public void init() {
// 创建布隆过滤器,预计10000个元素,误报率0.01
bloomFilter = BloomFilter.create(
Funnels.stringFunnel(Charset.defaultCharset()),
10000,
0.01
);
// 加载白名单
loadWhitelist();
}
private void loadWhitelist() {
// 从配置文件或数据库加载白名单IP
List<String> whitelistIps = getWhitelistIps();
for (String ip : whitelistIps) {
bloomFilter.put(ip);
}
}
public boolean mightContain(String ip) {
// 布隆过滤器可能误判,需要二次确认
if (bloomFilter.mightContain(ip)) {
return exactCheck(ip); // 二次精确检查
}
return false;
}
private boolean exactCheck(String ip) {
// 在白名单中做精确查找
return true; // 简化实现
}
}
application.yml配置示例
whitelist:
ips:
- 192.168.1.1
- 10.0.0.1
- 172.16.0.1
patterns:
- 192.168.1.*
- 10.0.*.1
最佳实践建议
- 缓存机制:使用Redis或本地缓存减少数据库查询
- 动态更新:支持运行时动态添加/移除白名单
- 日志记录:记录所有白名单校验的请求
- 性能监控:监控校验耗时,优化热点路径
- 降级策略:当白名单服务不可用时,有合理的降级方案
选择哪种方案取决于你的具体需求:
- 小型应用:使用Set或List
- 中型应用:使用注解+AOP
- 大型应用:使用数据库+缓存+布隆过滤器
需要根据你的实际场景选择合适的实现方式。