分别用Sentinel和HotKey统计访问数实现反爬虫
- HotKey实现
将HotKey相关的代码和jar包启动后通过http://127.0.0.1:8121 来到HotKey的控制台。在原来的配置中加上这段配置
▼js复制代码```{ "duration": 60, "key": "anti_crawler_", "prefix": true, "interval": 60, "threshold": 20, "desc": "反爬虫缓存" },
然后基于之前的代码进行修改(我的反爬虫是基于注解实现的,参考上篇文章)
▼text复制代码@Aspect @Component public class LimitAspect { @Resource private CounterManager counterManager; @Resource private HotKeyCounterManager hotKeyCounterManager; @Resource private UserService userService; @Before("@annotation(limitCheck)") public void beforeMethod(LimitCheck limitCheck){ RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest(); // 显式获取当前登录用户 User loginUser = userService.getLoginUser(request); if (loginUser == null) { throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR); } String ip = IpUtils.getClientIp(request); crawlerDetect(loginUser.getId(), ip); } /** * 检测爬虫 * @param loginUserId */ private void crawlerDetect(long loginUserId, String ip) { // 拼接访问 key String key = String.format("user:access:%s", loginUserId); // 一分钟内访问次数,达到配置设置的阈值为true,否则为false Boolean result = hotKeyCounterManager.incrAndGetCounter(key); // 是否封号 if (result) { // 踢下线 StpUtil.kickout(loginUserId); // 将ip更新到nacos配置的黑名单 updateNacosBlackList(ip); throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "访问太频繁,已被封号"); } } @Value("${nacos.config.data-id}") private String dataId; @Value("${nacos.config.server-addr}") private String serverAddr; @Value("${nacos.config.group}") private String group; private void updateNacosBlackList(String ip){ Properties properties = new Properties(); properties.put("serverAddr", serverAddr); try { // 创建ConfigService ConfigService configService = NacosFactory.createConfigService(properties); // 从Nacos获取配置 String content = configService.getConfig(dataId, group, 5000); System.out.println("获取到的YAML配置内容: " + content); // 解析YAML Yaml yaml = new Yaml(); Map<String, Object> yamlMap = yaml.load(content); // 获取黑名单IP列表 List<String> blacklist = (List<String>) yamlMap.get("blackIpList"); // 添加新的IP到黑名单 if (!blacklist.contains(ip)) { blacklist.add(ip); System.out.println("添加新的IP到黑名单: " + ip); } // 将更新后的YAML内容发布到Nacos String updatedContent = yaml.dump(yamlMap); boolean isPublishOk = configService.publishConfig(dataId, group, updatedContent); if (isPublishOk) { System.out.println("配置更新成功"); } else { System.out.println("配置更新失败"); } } catch (NacosException e) { e.printStackTrace(); } } }
基于HotKey统计访问数,根据配置的阈值判断是否为爬虫用户
▼text复制代码@Slf4j @Service public class HotKeyCounterManager { /** * 增加并返回计数 */ public Boolean incrAndGetCounter(String key) { // 根据时间粒度生成 redisKey String redisKey ="anti_crawler_" + key; // 如果是热 key(访问过于频繁) 返回true,否则为false return JdHotKeyStore.isHotKey(redisKey); } }
去nacos控制台查看ip已经在黑名单。

- Sentinel实现
▼text复制代码@Value("${nacos.config.data-id}") private String dataId; @Value("${nacos.config.server-addr}") private String serverAddr; @Value("${nacos.config.group}") private String group; private void updateNacosBlackList(String ip){ Properties properties = new Properties(); properties.put("serverAddr", serverAddr); try { // 创建ConfigService ConfigService configService = NacosFactory.createConfigService(properties); // 从Nacos获取配置 String content = configService.getConfig(dataId, group, 5000); System.out.println("获取到的YAML配置内容: " + content); // 解析YAML Yaml yaml = new Yaml(); Map<String, Object> yamlMap = yaml.load(content); // 获取黑名单IP列表 List<String> blacklist = (List<String>) yamlMap.get("blackIpList"); // 添加新的IP到黑名单 if (!blacklist.contains(ip)) { blacklist.add(ip); System.out.println("添加新的IP到黑名单: " + ip); } // 将更新后的YAML内容发布到Nacos String updatedContent = yaml.dump(yamlMap); boolean isPublishOk = configService.publishConfig(dataId, group, updatedContent); if (isPublishOk) { System.out.println("配置更新成功"); } else { System.out.println("配置更新失败"); } } catch (NacosException e) { e.printStackTrace(); } } /** * getQuestionVOById * 监控用户访问接口频率,发现爬虫用户直接封号,当访问数达到阈值就会执行这段代码 */ public BaseResponse<QuestionVO> handleBlockException(long id, HttpServletRequest request, BlockException ex) { User loginUser = userService.getLoginUser(request); String ip = IpUtils.getClientIp(request); // 踢下线 StpUtil.kickout(loginUser.getId()); // ip更新到nacos updateNacosBlackList(ip); // 限流操作 return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "访问太频繁,已封号"); } /** * 根据 id 获取题目(封装类) * * @param id * @return */ @GetMapping("/get/vo") @SentinelResource(value = "getQuestionVOById", blockHandler = "handleBlockException") public BaseResponse<QuestionVO> getQuestionVOById(long id, HttpServletRequest request) { ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR); // 查询数据库 Question question = questionService.getById(id); ThrowUtils.throwIf(question == null, ErrorCode.NOT_FOUND_ERROR); return ResultUtils.success(questionService.getQuestionVO(question, request)); }
用编码的方式配置规则
▼text复制代码@Component public class SentinelRulesManager { @PostConstruct public void initRules() { initFlowRule(); } public void initFlowRule() { // 单 IP 查看题目反爬虫规则 ParamFlowRule rule = new ParamFlowRule("getQuestionVOById") .setParamIdx(0) // 对第 0 个参数限流,即 IP 地址 .setCount(10) // 每分钟最多 10 次 .setDurationInSec(60); // 规则的统计周期为 60 秒 ParamFlowRuleManager.loadRules(Collections.singletonList(rule)); } }
Sentinel控制台中getQuestionVOById的热点参数限流规则
测试达到阈值直接封号

评论
问答助学
相关内容
0个评论
全部评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
