后端请求通过拦截器增加ip黑名单功能
一、条件
需要能在请求头中带上客户请求的真实IP,我这边是通过Nginx给所有请求的header增加真实的IP地址
可以通过X-Forwarded-For获取真实IP
location ^~/admin/ {
limit_req zone=mylimit burst=5 nodelay;
limit_req_log_level error;
limit_req_status 503;
proxy_connect_timeout 3s; # 无缝切换其他服务
proxy_pass http://sob-blog-server;
#以下是一些反向代理的配置可删除
proxy_redirect off;
#后端的Web服务器可以通过X-Forwarded-For获取用户真实IP
proxy_set_header Host $host;
proxy_set_header Cookie $http_cookie;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header HTTP_X_FORWARDED_FOR $remote_addr;
proxy_set_header X-Forwarded-Server $host;
}
二、编写拦截器类
package net.sunofbeach.blog.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import net.sunofbeach.blog.exception.ServiceException;
import net.sunofbeach.blog.response.ResponseResult;
import net.sunofbeach.blog.utils.Constants;
import net.sunofbeach.blog.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Pattern;
/**
* @PackageName:net.sunofbeach.blog.config
* @ClassName: BlacklistInterceptor
* @Description:
* @author: guoyoucong
* @date 2023/8/17 22:15
*/
@Component
@Slf4j
public class BlacklistInterceptor implements HandlerInterceptor {
@Autowired
RedisUtils redisUtils;
@Autowired
private Gson gson;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String ip = request.getHeader("X-Real-Ip");
String clientIP = request.getHeader("X-Forwarded-For");
if (clientIP == null || clientIP.length() == 0 || "unknown".equalsIgnoreCase(clientIP)) {
clientIP = ip;
log.info("X-Real-Ip地址为:{}", clientIP);
}
if (clientIP == null || clientIP.length() == 0 || "unknown".equalsIgnoreCase(clientIP)) {
return true;
}
log.info("ip地址为:{}", ip);
log.info("clientIP地址为:{}", clientIP);
String method = request.getMethod();
try {
checkIp(clientIP, method, request.getRequestURI());
} catch (Exception e) {
log.error("ip地址为:{},访问接口:{},发生异常:{}", clientIP, request.getRequestURI(), e.getMessage());
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
ResponseResult failed = ResponseResult.FAILED(e.getMessage());
PrintWriter writer = response.getWriter();
writer.write(gson.toJson(failed));
writer.flush();
return false;
}
return true;
}
public void checkIp(String ipAddress, String method, String requestURI) {
if (!isIpAddress(ipAddress)) {
// redisUtils.set(Constants.BlackIP.BLACK_IP+ipAddress,new Date(),60*60*24*7);
throw new ServiceException("IP地址格式不正确");
}
String post = "GET".equalsIgnoreCase(method) ? "GET" : "POST";
int min = 5;
if (post.equals("GET")) {
min = 3;
}
String apiKey = Constants.BlackIP.BLACK_IP_API + post + "_" + requestURI + "_" + ipAddress;
String countKey = Constants.BlackIP.BLACK_IP_COUNT_API + post + "_" + requestURI + "_" + ipAddress;
if (redisUtils.hasKey(apiKey)) {
throw new ServiceException("恭喜你进入了小黑屋!!!惩罚你" + min + "分钟不能访问!!!");
}
if (redisUtils.hasKey(countKey)) {
Integer index = (Integer) redisUtils.get(countKey);
//数量大于1秒15次加入黑名单
if (index > 6) {
redisUtils.set(apiKey, sdf.format(new Date()), 60 * min);
JSONObject json = new JSONObject();
json.put("ip", ipAddress);
json.put("method", method);
json.put("requestURI", requestURI);
json.put("time", sdf.format(new Date()));
//加入黑名单列表 永久记录
redisUtils.lSet(Constants.BlackIP.BLACK_IP_LIST, json);
throw new ServiceException("恭喜你进入了小黑屋!!!惩罚你" + min + "分钟不能访问!!!");
}
redisUtils.set(countKey, ++index, 3);
} else {
redisUtils.set(countKey, 1, 3);
}
}
public boolean isIpAddress(String value) {
Pattern pattern = Pattern.compile("^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]"
+ "|[*])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]|[*])$");
return pattern.matcher(value).matches();
}
}
三、注册拦截器
package net.sunofbeach.blog.config;
import net.sunofbeach.blog.interceptor.ApiInterceptor;
import net.sunofbeach.blog.interceptor.BlacklistInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
@Autowired
private BlacklistInterceptor blacklistInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(blacklistInterceptor).addPathPatterns("/**")
.excludePathPatterns("/public/**"); ;
}
}
四、成功拦截
五、后话
欢迎大家攻击我的网站,哈哈哈哈哈哈哈哈
本文由
loneliness
原创发布于
阳光沙滩
,未经作者授权,禁止转载