SpringBoot流量限制,黑名单,只允许本机访问

原理:
自定义注解类和拦截器,当用户访问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();
	}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值