导读
由于项目需要,这里需要设计一个IP白名单,增加接口安全。SpringBoot项目,因为接口已经开发好,在不变动接口的情况下,采用拦截器做处理,思路是拦截指定的请求,获取到该请求的IP,然后根据该ip去配置文件查询是否存在,如果存在就允许访问,否则没有权限访问。
使用
创建配置文件ip_address.yml
address:ipFilters:- ip: 127.0.0.1whitelists: true- ip: 192.168.255.255whitelists: true
创建读取配置YML文件属性PropConfig
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;import org.springframework.core.io.ClassPathResource;import java.util.Objects;/*** 读取配置文件*/@Configurationpublic class PropConfig {@Beanpublic static PropertySourcesPlaceholderConfigurer properties() {PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();yaml.setResources(new ClassPathResource("ip_address.yml"));configurer.setProperties(Objects.requireNonNull(yaml.getObject()));return configurer;}}
创建实体类和IPAddress类
- 实体类IpFilter ```java import java.io.Serializable;
/**
ip过滤器实体类 */ public class IpFilter implements Serializable {
private static final long serialVersionUID = 8802493743077425037L; /**
ip地址 */ private String ip;
/**
白名单—true,黑名单—false */ private Boolean whitelists;
public String getIp() { return ip; }
public void setIp(String ip) { this.ip = ip; }
public Boolean getWhitelists() { return whitelists; }
public void setWhitelists(Boolean whitelists) { this.whitelists = whitelists; } }
- **IpAddress类**```javaimport org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.Configuration;import java.io.Serializable;import java.util.List;/*** IpAddress 存放集合属性*/@Configuration@ConfigurationProperties("address")@Datapublic class IpAddress implements Serializable {private static final long serialVersionUID = -1686798098991604714L;private List<IpFilter> ipFilters; //ipFilters与yml文件的集合属性对应}
获取工具类IPUtils
import javax.servlet.http.HttpServletRequest;/*** 获取用户的IP*/public class IPUtils {/*** 本地IP localhost*/private static final String NATIVEIP = "0:0:0:0:0:0:0:1";/*** 获取用户真实IP地址,不使用request.getRemoteAddr()的原因是有可能用户使用了代理软件方式避免真实IP地址,* 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值** @return ip*/public static String getRealIP(HttpServletRequest request) {String ip = request.getHeader("x-forwarded-for");if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {// 多次反向代理后会有多个ip值,第一个ip才是真实ipif (ip.indexOf(",") != -1) {ip = ip.split(",")[0];}}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");System.out.println("Proxy-Client-IP ip: " + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");System.out.println("WL-Proxy-Client-IP ip: " + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_CLIENT_IP");System.out.println("HTTP_CLIENT_IP ip: " + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_X_FORWARDED_FOR");System.out.println("HTTP_X_FORWARDED_FOR ip: " + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("X-Real-IP");System.out.println("X-Real-IP ip: " + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();System.out.println("getRemoteAddr ip: " + ip);}if (ip.equals(NATIVEIP)) {ip = "127.0.0.1";System.out.println("get native ip" + ip);}return ip;}}
拦截器IPInterceptor
import com.demo.common.entity.IpAddress;import com.demo.common.entity.IpFilter;import com.demo.common.utils.IPUtils;import org.apache.commons.lang3.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.util.List;/*** IP 拦截器** @author hyanchao* @create 2020/1/14 14:23*/@Componentpublic class IPInterceptor implements HandlerInterceptor {private static Logger LOG = LoggerFactory.getLogger(IPInterceptor.class);@Autowiredprivate IpAddress address;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//过滤ip,若用户在白名单内,则放行String ipAddress = IPUtils.getRealIP(request);LOG.info("USER IP ADDRESS IS =>" + ipAddress);if (!StringUtils.isNotBlank(ipAddress)) {response.getWriter().append("<h1 style=\"text-align:center;\">Not allowed!</h1>");return false;}//读取ip地址白名单集合,如果存在,并且为白名单,则放行,不然就拦截List<IpFilter> ipFilters = address.getIpFilters();if (ipFilters != null) {for (IpFilter ips : ipFilters) {if (ipAddress.equals(ips.getIp()) && ips.getWhitelists()) {return true;}}}response.getWriter().append("<h1 style=\"text-align:center;\">Not allowed!</h1>");return false;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}}
全局配置拦截
import com.demo.common.interceptor.IPInterceptor;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.CorsRegistry;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;@Configurationpublic class GlobalCorsConfig extends WebMvcConfigurerAdapter {@Autowiredprivate IPInterceptor ipInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 添加拦截器,配置拦截地址,这里拦截以api请求的接口比如http://localhost/api/getUserregistry.addInterceptor(ipInterceptor).addPathPatterns("/api/**");}}
END
这里还没有做限流处理,下次考虑试下限流。
