导读
由于项目需要,这里需要设计一个IP白名单,增加接口安全。SpringBoot项目,因为接口已经开发好,在不变动接口的情况下,采用拦截器做处理,思路是拦截指定的请求,获取到该请求的IP,然后根据该ip去数据库查询是否存在,如果存在就允许访问,否则没有权限访问。(PS:拦截器可以通过访问Service层,在service层添加Redis缓存,设置下缓存失效时间,这样就不需要每次访问接口就从数据库中查一遍,减轻数据库访问压力,点击查看Redis缓存配置,目前这里未设置Redis缓存)
使用
创建表ip_filter
CREATE TABLE `ip_filter` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '序号',
`ip` varchar(255) NOT NULL COMMENT 'ip地址',
`whitelists` tinyint(4) NOT NULL COMMENT '白名单0:黑名单1',
`module` varchar(255) DEFAULT NULL COMMENT '模块',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`modify_time` datetime DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='IP白名单表';
创建实体类和Mapper查询接口
- 实体类IpFilter ```java
/**
ip过滤器实体类 */ @Data public class IpFilter implements Serializable {
private static final long serialVersionUID = 8802493743077425037L; /**
ip地址 */ private String ip;
/**
白名单—0,黑名单—1 */ private Integer whitelists;
/**
模块 */ private String module;
/**
创建时间 */ private Date createTime;
/**
- 修改时间 */ private Date modifyTime; }
- **Mapper接口IpFilterMapper**
```java
import com.demo.common.entity.IpFilter;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Component;
import java.util.List;
@Mapper
@Component
public interface IpFilterMapper {
/**
* 从数据库中查询IP是否存在
*
* @param ipFilter
* @return
*/
@Select("select * from ip_filter where ip = #{query.ip} and whitelists=#{query.whitelists}")
@Results(
id = "ipFilterMessage",
value = {
@Result(column = "ip", property = "ip"),
@Result(column = "module", property = "module"),
@Result(column = "whitelists", property = "whitelists"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "modifyTime", property = "modify_time")
}
)
List<IpFilter> selectIpFilterByRealIp(@Param("query") IpFilter ipFilter);
}
获取工具类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才是真实ip
if (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.IpFilter;
import com.demo.common.mapper.IpFilterMapper;
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 拦截器
*/
@Component
public class IPInterceptor implements HandlerInterceptor {
private static Logger LOG = LoggerFactory.getLogger(IPInterceptor.class);
@Autowired
private IpFilterMapper ipFilterMapper;
@Override
public 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)) {
return false;
}
IpFilter ipFilter = new IpFilter();
//可以访问模块
ipFilter.setModule("test");
//ip地址
ipFilter.setIp(ipAddress);
//白名单
ipFilter.setWhitelists(0);
List<IpFilter> ips = ipFilterMapper.selectIpFilterByRealIp(ipFilter);
if (ips.isEmpty()) {
response.getWriter().append("<h1 style=\"text-align:center;\">Not allowed!</h1>");
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public 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;
@Configuration
public class GlobalCorsConfig extends WebMvcConfigurerAdapter {
@Autowired
private IPInterceptor ipInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加拦截器,配置拦截地址,这里拦截以api请求的接口比如http://localhost/api/getUser
registry.addInterceptor(ipInterceptor).addPathPatterns("/api/**");
}
/**
//跨域请求拦截器
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowCredentials(true)
.allowedHeaders("*")
.allowedOrigins("*")
.allowedMethods("*");
}*/
}
END
这里还没有做限流处理,下次考虑试下限流。