在Spring Boot中实现支持多种IP格式的IP白名单过滤功能
在现代Web应用程序中,基于IP的访问控制是一种常见且有效的安全措施。本文将详细介绍如何在Spring Boot中实现IP白名单过滤功能,支持以下多种IP格式:
- 单个IP地址:192.168.1.1
- 通配符IP:192.168.2.*
- CIDR表示法:10.0.0.1/29
- IP范围:10.0.0.1-10.0.0.255
- IPv6地址:2001:0db8::1
- IPv6通配符:2001:0db8::*
项目初始化
1. 创建Spring Boot项目
使用您喜欢的方式(如Spring Initializr或IDE)创建一个新的Spring Boot项目,添加以下依赖:
- Spring Web
2. 添加必要的依赖
为了处理IP地址的解析和匹配,我们需要添加以下依赖到pom.xml:
<dependencies>
<!-- 其他依赖 -->
<!-- Apache Commons Net用于处理CIDR和IP范围 -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.8.0</version>
</dependency>
<!-- Guava用于处理IP地址 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
</dependencies>
请确保根据项目的实际情况调整版本号。
创建IP白名单过滤器
1. 定义白名单配置
在application.properties或application.yml中添加IP白名单配置,以便可以轻松地更改和管理。
使用application.properties:
ip.whitelist=192.168.1.1,192.168.2.*,10.0.0.1/29,10.0.0.1-10.0.0.255,2001:0db8::1,2001:0db8::*
使用application.yml:
ip:
whitelist: 192.168.1.1,192.168.2.*,10.0.0.1/29,10.0.0.1-10.0.0.255,2001:0db8::1,2001:0db8::*
2. 解析和存储IP模式
创建一个类IpWhitelistUtil,用于解析白名单配置并检查IP地址是否在白名单中。
package com.example.demo.util;
import com.google.common.net.InetAddresses;
import org.apache.commons.net.util.SubnetUtils;
import java.math.BigInteger;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
public class IpWhitelistUtil {
private List<IpPattern> patterns = new ArrayList<>();
public IpWhitelistUtil(String whitelist) {
String[] ips = whitelist.split(",");
for (String ip : ips) {
patterns.add(new IpPattern(ip.trim()));
}
}
public boolean isWhitelisted(String ipAddress) {
InetAddress inetAddress = InetAddresses.forString(ipAddress);
for (IpPattern pattern : patterns) {
if (pattern.matches(inetAddress)) {
return true;
}
}
return false;
}
}
创建一个IpPattern类,用于表示不同的IP格式并实现匹配逻辑。
package com.example.demo.util;
import com.google.common.net.InetAddresses;
import org.apache.commons.net.util.SubnetUtils;
import java.math.BigInteger;
import java.net.InetAddress;
import java.util.regex.Pattern;
public class IpPattern {
private String pattern;
private Pattern regexPattern;
private SubnetUtils subnetUtils;
private InetAddress startIp;
private InetAddress endIp;
private boolean isSingleIp;
private boolean isWildcard;
private boolean isCidr;
private boolean isRange;
private boolean isIpv6Wildcard;
public IpPattern(String pattern) {
this.pattern = pattern;
if (pattern.contains("*")) {
if (pattern.contains(":")) {
isIpv6Wildcard = true;
String regex = pattern.replace("*", ".*");
regexPattern = Pattern.compile(regex);
} else {
isWildcard = true;
String regex = pattern.replace(".", "\\.").replace("*", "\\d{1,3}");
regexPattern = Pattern.compile("^" + regex + "$");
}
} else if (pattern.contains("/")) {
isCidr = true;
subnetUtils = new SubnetUtils(pattern);
subnetUtils.setInclusiveHostCount(true);
} else if (pattern.contains("-")) {
isRange = true;
String[] parts = pattern.split("-");
startIp = InetAddresses.forString(parts[0]);
endIp = InetAddresses.forString(parts[1]);
} else {
isSingleIp = true;
startIp = InetAddresses.forString(pattern);
}
}
public boolean matches(InetAddress inetAddress) {
if (isSingleIp) {
return inetAddress.equals(startIp);
} else if (isWildcard) {
return regexPattern.matcher(inetAddress.getHostAddress()).matches();
} else if (isIpv6Wildcard) {
return regexPattern.matcher(inetAddress.getHostAddress()).matches();
} else if (isCidr) {
return subnetUtils.getInfo().isInRange(inetAddress.getHostAddress());
} else if (isRange) {
BigInteger ipVal = new BigInteger(1, inetAddress.getAddress());
BigInteger startIpVal = new BigInteger(1, startIp.getAddress());
BigInteger endIpVal = new BigInteger(1, endIp.getAddress());
return ipVal.compareTo(startIpVal) >= 0 && ipVal.compareTo(endIpVal) <= 0;
}
return false;
}
}
3. 实现过滤逻辑
创建一个IpWhitelistFilter类,实现javax.servlet.Filter接口。
package com.example.demo.filter;
import com.example.demo.util.IpWhitelistUtil;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class IpWhitelistFilter implements Filter {
private IpWhitelistUtil ipWhitelistUtil;
public IpWhitelistFilter(IpWhitelistUtil ipWhitelistUtil) {
this.ipWhitelistUtil = ipWhitelistUtil;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化逻辑(如果需要)
}
@Override
public void destroy() {
// 清理逻辑(如果需要)
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String clientIp = getClientIp(httpRequest);
if (ipWhitelistUtil.isWhitelisted(clientIp)) {
chain.doFilter(request, response);
} else {
httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden");
}
}
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();
}
// 处理多级代理的情况
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
return ip;
}
}
注册过滤器
在Spring Boot应用中,我们需要注册自定义过滤器。创建一个配置类FilterConfig。
package com.example.demo.config;
import com.example.demo.filter.IpWhitelistFilter;
import com.example.demo.util.IpWhitelistUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Value("${ip.whitelist}")
private String ipWhitelist;
@Bean
public FilterRegistrationBean<IpWhitelistFilter> ipWhitelistFilter() {
FilterRegistrationBean<IpWhitelistFilter> registrationBean = new FilterRegistrationBean<>();
IpWhitelistUtil ipWhitelistUtil = new IpWhitelistUtil(ipWhitelist);
registrationBean.setFilter(new IpWhitelistFilter(ipWhitelistUtil));
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(1); // 设置过滤器顺序
return registrationBean;
}
}
测试过滤器功能
1. 创建测试控制器
创建一个简单的控制器来测试过滤器。
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("/test")
public String test() {
return "访问成功!";
}
}
2. 运行应用程序
启动Spring Boot应用程序,并尝试从不同的IP地址访问/test端点。
- 如果客户端IP在白名单中,将返回"访问成功!"。
- 如果不在白名单中,将返回403 Forbidden错误。
3. 使用工具测试
您可以使用工具(如Postman或curl)并伪造X-Forwarded-For头来模拟不同的客户端IP。
示例命令:
curl -H "X-Forwarded-For: 192.168.1.1" http://localhost:8080/test
结论
通过本文的步骤,我们在Spring Boot中实现了一个支持多种IP格式的IP白名单过滤器。该过滤器可以识别单个IP地址、通配符、CIDR表示法、IP范围以及IPv6地址和通配符。这增强了应用程序的安全性,使其只能被授权的IP地址访问。
您可以将以上内容复制并保存为Markdown文件,以便参考和使用。
版权声明:本文为原创文章,版权归 全栈开发技术博客 所有。
本文链接:https://www.lvtao.net/dev/java-springboot-ip-whitelist.html
转载时须注明出处及本声明