SpringBoot防⽌接⼝恶意多次请求的操作前⾔
刚写代码不就,还不能做深层次安全措施,今天研究了⼀下基本的防⽌接⼝多次恶意请求的⽅法。
思路
1:设置同⼀IP,⼀个时间段内允许访问的最⼤次数
2:记录所有IP单位时间内访问的次数
3:将所有被限制IP存到存储器
4:通过IP过滤访问请求
该demo只有后台Java代码,没有前端
代码
⾸先是获取IP的⼯具类
public class Ipsettings {
public static String getRemoteHost(HttpServletRequest request) {
String ipAddress = null;
//ipAddress = RemoteAddr();
ipAddress = Header("x-forwarded-for");
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = Header("Proxy-Client-IP");
}
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = Header("WL-Proxy-Client-IP");
}
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = RemoteAddr();
if(ipAddress.equals("127.0.0.1")){
//根据⽹卡取本机配置的IP
InetAddress inet=null;
springboot aoptry {
inet = LocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress= HostAddress();
}
}
//对于通过多个代理的情况,第⼀个IP为客户端真实IP,多个IP按照','分割
if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15
if(ipAddress.indexOf(",")>0){
ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));
}
}
return ipAddress;
}
}
其次是以及IP存储器
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@WebListener
public class MyApplicationListener implements ServletContextListener {
private Logger logger = Logger(MyApplicationListener.class);
@Override
public void contextInitialized(ServletContextEvent sce) {
logger.info("liting: contextInitialized");
ServletContext context = ServletContext();
// IP存储器
Map<String, Long[]> ipMap = new HashMap<String, Long[]>();
context.setAttribute("ipMap", ipMap);
// 限制IP存储器:存储被限制的IP信息
Map<String, Long> limitedIpMap = new HashMap<String, Long>();
context.setAttribute("limitedIpMap", limitedIpMap);
logger.info("ipmap:"+String()+";limitedIpMap:"+String()+"初始化成功。。。。。");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// TODO Auto-generated method stub
}
}
最后是具体规则设置
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebFilter(urlPatterns="/*")
public class IpFilter implements Filter{
/**
* 默认限制时间(单位:ms)
*/
private static final long LIMITED_TIME_MILLIS = 5 * 2 * 1000;
/**
* ⽤户连续访问最⾼阀值,超过该值则认定为恶意操作的IP,进⾏限制
*/
private static final int LIMIT_NUMBER = 2;
/**
* ⽤户访问最⼩安全时间,在该时间内如果访问次数⼤于阀值,则记录为恶意IP,否则视为正常访问
*/
private static final int MIN_SAFE_TIME = 5000;
private FilterConfig config;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
/* (non-Javadoc)
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) */
@SuppressWarnings("unchecked")
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
ServletContext context = ServletContext();
// 获取限制IP存储器:存储被限制的IP信息
Map<String, Long> limitedIpMap = (Map<String, Long>) Attribute("limitedIpMap");
// 过滤受限的IP
filterLimitedIpMap(limitedIpMap);
// 获取⽤户IP
String ip = RemoteHost(request);
//以下是处理限制IP的规则,可以⾃⼰写
// 判断是否是被限制的IP,如果是则跳到异常页⾯
if (isLimitedIP(limitedIpMap, ip)) {
long limitedTime = (ip) - System.currentTimeMillis();
// 剩余限制时间(⽤为从毫秒到秒转化的⼀定会存在些许误差,但基本可以忽略不计)
request.setAttribute("remainingTime", ((limitedTime / 1000) + (limitedTime % 1000 > 0 ? 1 : 0)));
//RequestDispatcher("/error/overLimitIP").forward(request, response);
return;
}
// 获取IP存储器
Map<String, Long[]> ipMap = (Map<String, Long[]>) Attribute("ipMap");
// 判断存储器中是否存在当前IP,如果没有则为初次访问,初始化该ip
// 如果存在当前ip,则验证当前ip的访问次数
// 如果⼤于限制阀值,判断达到阀值的时间,如果不⼤于[⽤户访问最⼩安全时间]则视为恶意访问,跳转到异常页⾯ if (ainsKey(ip)) {
Long[] ipInfo = (ip);
ipInfo[0] = ipInfo[0] + 1;
System.out.println("当前第[" + (ipInfo[0]) + "]次访问");
if (ipInfo[0] > LIMIT_NUMBER) {
Long ipAccessTime = ipInfo[1];
Long currentTimeMillis = System.currentTimeMillis();
if (currentTimeMillis - ipAccessTime <= MIN_SAFE_TIME) {
limitedIpMap.put(ip, currentTimeMillis + LIMITED_TIME_MILLIS);
request.setAttribute("remainingTime", LIMITED_TIME_MILLIS);
return;
} else {
initIpVisitsNumber(ipMap, ip);
}
}
} else {
initIpVisitsNumber(ipMap, ip);
System.out.println("您⾸次访问该⽹站");
}
context.setAttribute("ipMap", ipMap);
chain.doFilter(request, response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
/
**
* @Description 过滤受限的IP,剔除已经到期的限制IP
* @param limitedIpMap
*/
private void filterLimitedIpMap(Map<String, Long> limitedIpMap) {
if (limitedIpMap == null) {
return;
}
Set<String> keys = limitedIpMap.keySet();
Iterator<String> keyIt = keys.iterator();
long currentTimeMillis = System.currentTimeMillis();
while (keyIt.hasNext()) {
long expireTimeMillis = (());
if (expireTimeMillis <= currentTimeMillis) {
}
}
}
/**
* @Description 是否是被限制的IP
* @param limitedIpMap
* @param ip
* @return true : 被限制 | false : 正常
*/
private boolean isLimitedIP(Map<String, Long> limitedIpMap, String ip) {
if (limitedIpMap == null || ip == null) {
// 没有被限制
return false;
}
Set<String> keys = limitedIpMap.keySet();
Iterator<String> keyIt = keys.iterator();
while (keyIt.hasNext()) {
String key = ();
if (key.equals(ip)) {
// 被限制的IP
return true;
}
}
return false;
}
/**
* 初始化⽤户访问次数和访问时间
*
* @param ipMap
* @param ip
*/
private void initIpVisitsNumber(Map<String, Long[]> ipMap, String ip) {
Long[] ipInfo = new Long[2];
ipInfo[0] = 0L;// 访问次数
ipInfo[1] = System.currentTimeMillis();// 初次访问时间
ipMap.put(ip, ipInfo);
}
}
然后再在启动类上加上注解扫描配置包
@ServletComponentScan(basePackages="扫描刚才的MyApplicationListener")
补充:springboot和redis控制单位时间内同个ip访问同个接⼝的次数
注:本⽂中的修改于⽹上⼀个错误的例⼦,不知道为什么⼀个错误的例⼦还被⼈疯狂转载,还都标着原创。。。具体是那个这⾥就不指出了!
第⼀步:⾃定义⼀个注解
注:其实完全没必要(这样做的唯⼀好处就是每个接⼝与的访问限制次数都可以不⼀样)。。但是注解这个东西⾃从培训结束后没有在⽤到过,决定还是再复习下
d.redis_springboot_mybatis_mysql.limit;
import Ordered;
import annotation.Order;
import java.lang.annotation.*;
/**
* @Retention:注解的保留位置
* @Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码⽂件中不包含
* @Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码⽂件中存在,但运⾏时⽆法获得,
* @Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码⽂件中存在,在运⾏时可以通过反射获取到
*/
@Retention(RetentionPolicy.RUNTIME)
/**
* @Target:注解的作⽤⽬标
* @Target(ElementType.TYPE) //接⼝、类、枚举、注解
* @Target(ElementType.FIELD) //字段、枚举的常量
* @Target(ElementType.METHOD) //⽅法
* @Target(ElementType.PARAMETER) //⽅法参数
* @Target(ElementType.CONSTRUCTOR) //构造函数
* @Target(ElementType.LOCAL_VARIABLE) //局部变量
* @Target(ElementType.ANNOTATION_TYPE) //注解
* @Target(ElementType.PACKAGE) ///包
*/
@Target(ElementType.METHOD)
/**
* @Document 说明该注解将被包含在javadoc中
*/
@Documented
/
**
* Ordered接⼝是由spring提供的,为了解决相同接⼝实现类的优先级问题
*/
//最⾼优先级- - - 个⼈觉得这个在这⾥没必要加
//@order,使⽤注解⽅式使类的加载顺序得到控制
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface RequestTimes {
//单位时间允许访问次数 - - -默认值是2
int count() default 2;
//设置单位时间为1分钟 - - - 默认值是1分钟
long time() default 60 * 1000;
}
Ordered:
1、接⼝内容:我们可以打开这个接⼝查看它的源码
我们可以看到这个接⼝中只有⼀个⽅法两个属性,⼀个是int的最⼩值,另⼀个是int的最⼤值
2、OrderComparator接⼝: PriorityOrdered是个接⼝,是Ordered接⼝的⼦类,并没有实现任何⽅法
这个Comparator⽅法的逻辑⼤致是:
PriorityOrdered的优先级⾼于Ordered
如果两个都是Ordered或者PriorityOrdered就⽐较他们的order值,order值越⼤,优先级越⼩
第⼆步:定义⼀个aop
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论