原理:
自定义注解类和拦截器,当用户访问web通过controller时,拦截器判断是否包括自定义注解类
拦截器根据controller方法上的自定义注解,自动限制访问频率/是否允许访问/只允许本机访问
访问次数/黑名单列表保存在redis中
超过规定时间重新计算访问次数
根据访问IP实现黑名单功能
根据访问IP和服务器IP对比实现仅本机访问功能
ps:拦截配置建议分开写,因为return后面就不执行了
- SpringBoot拦截器
/**
* 拦截器
*
* @ClassName: MyWebAppConfigurer
* @author johnny
* @date 2018年6月3日
*
*/
@Configuration
public class MyWebAppConfigurer implements WebMvcConfigurer {
public static final Logger LOGGER = LogManager.getLogger(MyWebAppConfigurer.class);
@Autowired
private RequestInterceptor2 requestInterceptor2;
@Bean
public RequestInterceptor2 tokenVerifyInterceptor2() {
return new RequestInterceptor2();
}
/** 添加拦截器 **/
@Override
public void addInterceptors(InterceptorRegistry registry) {
LOGGER.info("配置拦截器");
registry.addInterceptor(requestInterceptor2);
registry.addInterceptor(tokenVerifyInterceptor2()).addPathPatterns("/**");
WebMvcConfigurer.super.addInterceptors(registry);
}
}
- 拦截配置
@Component
public class RequestInterceptor2 implements HandlerInterceptor {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {// 判断请求是否是属于方法的请求
HandlerMethod hm = (HandlerMethod) handler;
String hostAddress = InetAddress.getLocalHost().getHostAddress();// 服务器ip
String remoteAddress = getRemoteAddr(request);// 请求ip
String servletPath = request.getServletPath();//
AdminLimit adminLimit = hm.getMethodAnnotation(AdminLimit.class);
if (null != adminLimit) {// 看是否有该注解
if (hostAddress.equals(remoteAddress) || adminLimit.hostAddressIpv4().equals(remoteAddress) || adminLimit.hostAddressIpv6().equals(remoteAddress)) {
return true;
} else {
String msg = "{remoteAddress:" + remoteAddress + ",Code:200,Msg:紧允许服务器本机查看}";
Object obj = JSONObject.toJSON(msg);
response.getWriter().write(JSONObject.toJSONString(obj));
return false;
}
}
AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
if (null == accessLimit) {// 看是否有该注解
return true;
}
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(TimeZone.getTimeZone("GMT+0800"));
String startTime = sdf.format(date).trim();
int seconds = accessLimit.seconds();
int maxCount = accessLimit.maxCount();
String key1 = servletPath + ":" + remoteAddress;
String key2 = startTime + ":" + remoteAddress;
Integer count = (Integer) redisTemplate.opsForValue().get(key1);
Integer scan = (Integer) redisTemplate.opsForValue().get(key2);
// 统计当天访问次数
if (null == scan || -1 == scan) {
redisTemplate.opsForValue().set(key2, 1, 1, TimeUnit.DAYS);
} else {
scan++;
redisTemplate.opsForValue().set(key2, scan, 0);
}
// 黑名单
if (redisTemplate.opsForSet().isMember("blacklist", remoteAddress)) {// 是否在黑名单中
String msg = "{remoteAddress:" + remoteAddress + ",Code:200,Msg:你已进入黑名单,请联系管理员171471869@163.com!}";
Object obj = JSONObject.toJSON(msg);
response.getWriter().write(JSONObject.toJSONString(obj));
return false;
}
// 限制访问频率
if (null == count || -1 == count) {// 第一次访问,设置过期时间
redisTemplate.opsForValue().set(key1, 1, seconds, TimeUnit.SECONDS);
return true;
}
if (count < maxCount) {// 访问次数内
count++;
redisTemplate.opsForValue().set(key1, count, 0);
return true;
}
if (count >= maxCount) {// 超出访问次数
count++;
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
String msg = "{remoteAddress:" + remoteAddress + ",Code:200,Msg:操作过于频繁,请稍后再试}";
Object obj = JSONObject.toJSON(msg);
response.getWriter().write(JSONObject.toJSONString(obj));
if (count == maxCount + 5) {
redisTemplate.opsForValue().set(key1, 1, seconds, TimeUnit.SECONDS);
}
return false;
}
}
return true;
}
public String getRemoteAddr(HttpServletRequest request) {
String remoteAddress = request.getHeader("x-forwarded-for");
if (remoteAddress == null || remoteAddress.length() == 0 || "unknown".equalsIgnoreCase(remoteAddress)) {
remoteAddress = request.getHeader("Proxy-Client-IP");
}
if (remoteAddress == null || remoteAddress.length() == 0 || "unknown".equalsIgnoreCase(remoteAddress)) {
remoteAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (remoteAddress == null || remoteAddress.length() == 0 || "unknown".equalsIgnoreCase(remoteAddress)) {
remoteAddress = request.getHeader("HTTP_CLIENT_IP");
}
if (remoteAddress == null || remoteAddress.length() == 0 || "unknown".equalsIgnoreCase(remoteAddress)) {
remoteAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (remoteAddress == null || remoteAddress.length() == 0 || "unknown".equalsIgnoreCase(remoteAddress)) {
remoteAddress = request.getRemoteAddr();
}
return remoteAddress;
}
}
- redis序列化
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
public static final Logger LOGGER = LogManager.getLogger(RedisConfig.class);
@Autowired
RedisConnectionFactory factory;
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer<?> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
LOGGER.info("redis序列化");
return template;
}
}
- 自定义注解类
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
/**
* seconds():表示指定的时间内 maxCount():表示请求的次数 如60秒内只能请求60次
*/
public @interface AccessLimit {
int seconds() default 60;
int maxCount() default 60;
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface AdminLimit {
String hostAddressIpv4() default "127.0.0.1";
String hostAddressIpv6() default "0:0:0:0:0:0:0:1";
}
- controller
@Controller
public class ServiceController {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 展示页面
*
* @param model
* @return
*/
@GetMapping("/monitor/server")
@AdminLimit
public ModelAndView server() {
mav.setViewName("user/monitorServer");
return mav;
}
@GetMapping("/monitor/javaweb")
@AdminLimit
public ModelAndView javaweb() {
ModelAndView mav = new ModelAndView();
Set<Object> set = redisTemplate.opsForSet().members("blacklist");// 黑名单
mav.addObject("blackList", set);
mav.setViewName("user/monitorWeb");
return mav;
}
@ResponseBody
@RequestMapping("addToBlackList")
@AccessLimit(seconds = 10, maxCount = 5) // 10秒内 允许请求5次
public String add(String remoteAddr) {
redisTemplate.opsForSet().add("blacklist", remoteAddr);// 添加到黑名单中
return null;
}
@ResponseBody
@RequestMapping("removeFromBlackList")
@AccessLimit(seconds = 10, maxCount = 5) // 10秒内 允许请求5次
public String remove(String remoteAddr) {
redisTemplate.opsForSet().remove("blacklist", remoteAddr);// 从黑名单移除
return null;
}
@ResponseBody
@RequestMapping("getRedisTemplateData")
public String getRedisTemplateData() {
Date date = new Date();
JSONObject json = new JSONObject();
JSONArray jsonArray1 = new JSONArray();
JSONArray jsonArray2 = new JSONArray();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(TimeZone.getTimeZone("GMT+0800"));
String startTime = sdf.format(date);
Set<String> keys = redisTemplate.keys(startTime + ":*");// 当天访问次数
Set<Object> set = redisTemplate.opsForSet().members("blacklist");// 黑名单
for (String str : keys) {
Integer scanCount = (Integer) redisTemplate.opsForValue().get(str);
String remoteAddr = str.substring(11, str.length());
JSONObject jsonObject = new JSONObject();
jsonObject.put("remoteAddr", remoteAddr);
jsonObject.put("scanCount", scanCount);
jsonObject.put("isBlack", set.contains(remoteAddr));
jsonArray1.put(jsonObject);
}
for (Object o : set) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("blickAddr", o);
jsonArray2.put(jsonObject);
}
json.put("count", jsonArray1.length());
json.put("data1", jsonArray1);
json.put("data2", jsonArray2);
json.put("code", 200);
json.put("msg", "");
return json.toString();
}
}