• Api接口防攻击防刷注解实现


    定义注解

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface ApiAccessLimit {
        int seconds() default 5;
    
        int maxCount() default 5;
    }

    定义方法切面

    @Aspect
    @Component
    @Slf4j
    public class ApiAccessLimitAspect {
    
        @Autowired
        @Qualifier("redisUtil2")
        private RedisUtil redisUtil;
    
        @Before("@annotation(apiAccessLimit)")
        public void checkAccessLimit(JoinPoint joinPoint, ApiAccessLimit apiAccessLimit) {
            //获取request对象
            HttpServletRequest request = currentRequest();
            if (Objects.isNull(request)) {
                return;
            }
            String ipAddress = IpAddressUtil.getIPAddress(request);
            String method = joinPoint.getSignature().getName();
            Integer count = (Integer) redisUtil.get(ipAddress + method);
            if (count == null) {
                //第一次访问
                redisUtil.set(ipAddress + method, 1, apiAccessLimit.seconds());
            }
            else if (count < apiAccessLimit.maxCount()) {
                //加1
                redisUtil.increase(ipAddress + method, 1);
            }
            else {
                //超出访问次数
                throw new BaseException(ApiAccessErrorEnum.ACCESS_LIMIT_ERROR);
            }
        }
    
    
        /**
         * 获取当前请求信息
         * @return Current request or null
         */
        private HttpServletRequest currentRequest() {
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            return Optional.ofNullable(servletRequestAttributes).map(ServletRequestAttributes::getRequest).orElse(null);
        }
    }

    定义异常

    public enum ApiAccessErrorEnum implements IErrorCode {
    
        ACCESS_LIMIT_ERROR("10001", "Access Limit");
    
        private final String errorCode;
        private final String errorMessage;
        private static final String ERROR_CODE_START = "Access-";
    
        ApiAccessErrorEnum(String errorCode, String errorMessage) {
            this.errorCode = errorCode;
            this.errorMessage = errorMessage;
        }
    
        @Override
        public String getErrorCode() {
            return ERROR_CODE_START + errorCode;
        }
    
        @Override
        public String getErrorMessage() {
            return errorMessage;
        }
    }

    Redis配置类

    @Configuration
    public class RedisConfiguration {
    
        @Autowired
        private RedisProperties properties;
    
        @Bean("lettuceConnectionFactory")
        public LettuceConnectionFactory lettuceConnectionFactory() throws Exception {
            LettuceClientConfigurationBuilder lettuceClientConfigurationBuilder = LettuceClientConfiguration.builder();
    
            if (this.properties.getSentinel() != null) {
                return new LettuceConnectionFactory(getSentinelConfig(), lettuceClientConfigurationBuilder.build());
            }
    
            if (this.properties.getCluster() != null) {
                return new LettuceConnectionFactory(getClusterConfiguration(), lettuceClientConfigurationBuilder.build());
            }
    
            RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
            redisStandaloneConfiguration.setDatabase(properties.getDatabase());
            redisStandaloneConfiguration.setHostName(properties.getHost());
            redisStandaloneConfiguration.setPort(properties.getPort());
            redisStandaloneConfiguration.setPassword(RedisPassword.of(AesUtils.aesDecrypt(properties.getPassword())));
    
            return new LettuceConnectionFactory(redisStandaloneConfiguration, lettuceClientConfigurationBuilder.build());
        }
    
        private RedisClusterConfiguration getClusterConfiguration() throws Exception {
            RedisProperties.Cluster clusterProperties = this.properties.getCluster();
            RedisClusterConfiguration config = new RedisClusterConfiguration(
                    clusterProperties.getNodes());
            if (clusterProperties.getMaxRedirects() != null) {
                config.setMaxRedirects(clusterProperties.getMaxRedirects());
            }
            if (this.properties.getPassword() != null) {
                config.setPassword(RedisPassword.of(AesUtils.aesDecrypt(this.properties.getPassword())));
            }
            return config;
        }
    
        private RedisSentinelConfiguration getSentinelConfig() throws Exception {
            RedisSentinelConfiguration config = new RedisSentinelConfiguration();
            RedisProperties.Sentinel sentinelProperties = this.properties.getSentinel();
            config.master(sentinelProperties.getMaster());
            config.setSentinels(createSentinels(sentinelProperties));
            if (this.properties.getPassword() != null) {
                config.setPassword(RedisPassword.of(AesUtils.aesDecrypt(this.properties.getPassword())));
            }
            config.setDatabase(this.properties.getDatabase());
            return config;
        }
    
        private List<RedisNode> createSentinels(RedisProperties.Sentinel sentinel) {
            List<RedisNode> nodes = new ArrayList<>();
            for (String node : sentinel.getNodes()) {
                try {
                    String[] parts = StringUtils.split(node, ":");
                    Assert.state(parts.length == 2, "Must be defined as 'host:port'");
                    nodes.add(new RedisNode(parts[0], Integer.valueOf(parts[1])));
                }
                catch (RuntimeException ex) {
                    throw new IllegalStateException(
                            "Invalid redis sentinel " + "property '" + node + "'", ex);
                }
            }
            return nodes;
        }
    
        @Bean
        public RedisTemplate<String, Object> redisTemplate(@Qualifier("lettuceConnectionFactory") RedisConnectionFactory factory) {
            RedisTemplate<String, Object> template = new RedisTemplate<>();
            template.setConnectionFactory(factory);
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
            template.setKeySerializer(stringRedisSerializer);
            template.setHashKeySerializer(stringRedisSerializer);
            template.setValueSerializer(jackson2JsonRedisSerializer);
            template.setHashValueSerializer(jackson2JsonRedisSerializer);
            template.afterPropertiesSet();
            return template;
        }
    
        @Bean
        public RedisUtil redisUtil(RedisTemplate<String, Object> redisTemplate) {
            return new RedisUtil(redisTemplate);
        }
    }

    Redis工具类

    @Slf4j
    public final class RedisUtil {
    
        private RedisTemplate<String, Object> redisTemplate;
    
        public RedisUtil(RedisTemplate<String, Object> redisTemplate) {
            this.redisTemplate = redisTemplate;
        }
    
        /**
         * 指定缓存失效时间
         *
         * @param key  键
         * @param time 时间(秒)
         */
        private void expire(String key, long time) {
            try {
                if (time > 0) {
                    redisTemplate.expire(key, time, TimeUnit.SECONDS);
                }
            }
            catch (Exception e) {
                log.error("", e);
            }
        }
    
        /**
         * 指定缓存失效时间
         *
         * @param key  键
         * @param time 时间(秒)
         */
        public void expireAt(String key, Date time) {
            try {
                if (time != null && time.after(new Date())) {
                    redisTemplate.expireAt(key, time);
                }
            }
            catch (Exception e) {
                log.error("", e);
            }
        }
    
        /**
         * 根据key 获取过期时间
         *
         * @param key 键 不能为null
         * @return 时间(秒) 返回0代表为永久有效
         */
        public long getExpire(String key) {
            return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    
        }
    
        /**
         * 判断key是否存在
         *
         * @param key 键
         * @return true 存在 false不存在
         */
        public boolean hasKey(String key) {
            try {
                return redisTemplate.hasKey(key);
            }
            catch (Exception e) {
                log.error("", e);
                return false;
    
            }
    
        }
    
        /**
         * 删除缓存
         *
         * @param key 可以传一个值 或多个
         */
    
        @SuppressWarnings("unchecked")
        public void del(String... key) {
            if (key != null && key.length > 0) {
                if (key.length == 1) {
                    redisTemplate.delete(key[0]);
                }
                else {
                    redisTemplate.delete(CollectionUtils.arrayToList(key));
                }
            }
        }
    
        // ============================String=============================
    
        /**
         * 普通缓存获取
         *
         * @param key 键
         * @return*/
        public Object get(String key) {
            return key == null ? null : redisTemplate.opsForValue().get(key);
        }
    
        /**
         * 普通缓存放入
         *
         * @param key   键
         * @param value 值
         * @return true成功 false失败
         */
        public boolean set(String key, Object value) {
            try {
                redisTemplate.opsForValue().set(key, value);
                return true;
            }
            catch (Exception e) {
                log.error("", e);
                return false;
            }
        }
    
        /**
         * 普通缓存放入并设置时间
         *
         * @param key   键
         * @param value 值
         * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
         * @return true成功 false 失败
         */
        public boolean set(String key, Object value, long time) {
            try {
                if (time > 0) {
                    redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
                }
                else {
                    set(key, value);
                }
                return true;
            }
            catch (Exception e) {
                log.error("", e);
                return false;
            }
        }
    
        /**
         * 递增
         *
         * @param key   键
         * @param delta 要增加几(大于0)
         * @return
         */
        public long increase(String key, long delta) {
            if (delta < 0) {
                throw new RuntimeException("递增因子必须大于0");
            }
            return redisTemplate.opsForValue().increment(key, delta);
        }
    
        /**
         * 递减
         *
         * @param key   键
         * @param delta 要减少几(小于0)
         * @return
         */
        public long decrease(String key, long delta) {
            if (delta < 0) {
                throw new RuntimeException("递减因子必须大于0");
            }
            return redisTemplate.opsForValue().increment(key, -delta);
        }
    
        // ================================Map=================================
    
        /**
         * @param key  键 不能为null
         * @param item 项 不能为null
         * @return*/
        public Object hashget(String key, String item) {
            return redisTemplate.opsForHash().get(key, item);
        }
    
        /**
         * 获取hashKey对应的所有键值
         *
         * @param key 键
         * @return 对应的多个键值
         */
        public Map<Object, Object> getMap(String key) {
            return redisTemplate.opsForHash().entries(key);
    
        }
    
        /**
         * 176 HashSet 177
         *
         * @param key 键 178
         * @param map 对应多个键值 179
         * @return true 成功 false 失败 180
         */
    
        public boolean hmset(String key, Map<String, Object> map) {
            try {
                redisTemplate.opsForHash().putAll(key, map);
                return true;
            }
            catch (Exception e) {
                log.error("", e);
                return false;
    
            }
    
        }
    
        /**
         * HashSet 并设置时间
         *
         * @param key  键
         * @param map  对应多个键值
         * @param time 时间(秒)
         * @return true成功 false失败
         */
    
        public boolean hmset(String key, Map<String, Object> map, long time) {
            try {
                redisTemplate.opsForHash().putAll(key, map);
                if (time > 0) {
                    expire(key, time);
                }
                return true;
            }
            catch (Exception e) {
                log.error("", e);
                return false;
    
            }
    
        }
    
        /**
         * 212 向一张hash表中放入数据,如果不存在将创建 213
         *
         * @param key   键 214
         * @param item  项 215
         * @param value 值 216
         * @return true 成功 false失败 217
         */
    
        public boolean hset(String key, String item, Object value) {
            try {
                redisTemplate.opsForHash().put(key, item, value);
                return true;
            }
            catch (Exception e) {
                log.error("", e);
                return false;
            }
    
        }
    
        /**
         * 229 向一张hash表中放入数据,如果不存在将创建 230
         *
         * @param key   键 231
         * @param item  项 232
         * @param value 值 233
         * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 234
         * @return true 成功 false失败 235
         */
    
        public boolean hset(String key, String item, Object value, long time) {
            try {
                redisTemplate.opsForHash().put(key, item, value);
                if (time > 0) {
                    expire(key, time);
                }
                return true;
            }
            catch (Exception e) {
                log.error("", e);
                return false;
            }
        }
    
        /**
         * 250 删除hash表中的值 251
         *
         * @param key  键 不能为null 252
         * @param item 项 可以使多个 不能为null 253
         */
    
        public void hdel(String key, Object... item) {
            redisTemplate.opsForHash().delete(key, item);
        }
    
        /**
         * 259 判断hash表中是否有该项的值 260
         *
         * @param key  键 不能为null 261
         * @param item 项 不能为null 262
         * @return true 存在 false不存在 263
         */
    
        public boolean hHasKey(String key, String item) {
            return redisTemplate.opsForHash().hasKey(key, item);
    
        }
    
        /**
         * 269 hash递增 如果不存在,就会创建一个 并把新增后的值返回 270
         *
         * @param key  键 271
         * @param item 项 272
         * @param by   要增加几(大于0) 273
         * @return 274
         */
    
        public double hincr(String key, String item, double by) {
            return redisTemplate.opsForHash().increment(key, item, by);
        }
    
        /**
         * 280 hash递减 281
         *
         * @param key  键 282
         * @param item 项 283
         * @param by   要减少记(小于0) 284
         * @return 285
         */
    
        public double hdecr(String key, String item, double by) {
            return redisTemplate.opsForHash().increment(key, item, -by);
        }
    
        // ============================set=============================
    
        /**
         * 292 根据key获取Set中的所有值 293
         *
         * @param key 键 294
         * @return 295
         */
    
        public Set<Object> sGet(String key) {
            try {
                return redisTemplate.opsForSet().members(key);
            }
            catch (Exception e) {
                log.error("", e);
                return null;
    
            }
    
        }
    
        /**
         * 306 根据value从一个set中查询,是否存在 307
         *
         * @param key   键 308
         * @param value 值 309
         * @return true 存在 false不存在 310
         */
    
        public boolean sHasKey(String key, Object value) {
            try {
                return redisTemplate.opsForSet().isMember(key, value);
            }
            catch (Exception e) {
                log.error("sHasKey", e);
                return false;
            }
    
        }
    
        /**
         * 321 将数据放入set缓存 322
         *
         * @param key    键 323
         * @param values 值 可以是多个 324
         * @return 成功个数 325
         */
    
        public long sSet(String key, Object... values) {
    
            try {
    
                return redisTemplate.opsForSet().add(key, values);
    
            }
            catch (Exception e) {
                log.error("sSet", e);
                return 0;
    
            }
    
        }
    
        /**
         * 336 将set数据放入缓存 337
         *
         * @param key    键 338
         * @param time   时间(秒) 339
         * @param values 值 可以是多个 340
         * @return 成功个数 341
         */
    
        public long sSetAndTime(String key, long time, Object... values) {
            try {
                Long count = redisTemplate.opsForSet().add(key, values);
                if (time > 0) {
                    expire(key, time);
                }
                return count;
            }
            catch (Exception e) {
                log.error("", e);
                return 0;
            }
        }
    
        /**
         * 355 获取set缓存的长度 356
         *
         * @param key 键 357
         * @return 358
         */
    
        public long sGetSetSize(String key) {
            try {
                return redisTemplate.opsForSet().size(key);
            }
            catch (Exception e) {
                log.error("", e);
                return 0;
            }
        }
    
        /**
         * 369 移除值为value的 370
         *
         * @param key    键 371
         * @param values 值 可以是多个 372
         * @return 移除的个数 373
         */
    
        public long setRemove(String key, Object... values) {
            try {
                Long count = redisTemplate.opsForSet().remove(key, values);
                return count;
            }
            catch (Exception e) {
                log.error("setRemove", e);
                return 0;
            }
        }
    
        // ===============================list=================================
    
        /**
         * 386 获取list缓存的内容 387
         *
         * @param key   键 388
         * @param start 开始 389
         * @param end   结束 0 到 -1代表所有值 390
         * @return 391
         */
    
        public List<Object> lGet(String key, long start, long end) {
            try {
                return redisTemplate.opsForList().range(key, start, end);
            }
            catch (Exception e) {
                log.error("", e);
                return null;
            }
        }
    
        /**
         * 402 获取list缓存的长度 403
         *
         * @param key 键 404
         * @return 405
         */
    
        public long lGetListSize(String key) {
            try {
                return redisTemplate.opsForList().size(key);
            }
            catch (Exception e) {
                log.error("", e);
                return 0;
            }
        }
    
        /**
         * 416 通过索引 获取list中的值 417
         *
         * @param key   键 418
         * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 419
         * @return 420
         */
    
        public Object lGetIndex(String key, long index) {
            try {
                return redisTemplate.opsForList().index(key, index);
            }
            catch (Exception e) {
                log.error("", e);
                return null;
            }
    
        }
    
        /**
         * 431 将list放入缓存 432
         *
         * @param key   键 433
         * @param value 值 434
         * @return 436
         */
        public boolean lSet(String key, Object value) {
            try {
                redisTemplate.opsForList().rightPush(key, value);
                return true;
            }
            catch (Exception e) {
                log.error("", e);
                return false;
            }
        }
    
        /**
         * 将list放入缓存
         *
         * @param key   键
         * @param value 值
         * @param time  时间(秒)
         * @return
         */
        public boolean lSet(String key, Object value, long time) {
            try {
                redisTemplate.opsForList().rightPush(key, value);
                if (time > 0) {
                    expire(key, time);
                }
                return true;
            }
            catch (Exception e) {
                log.error("", e);
                return false;
    
            }
    
        }
    
        /**
         * 467 将list放入缓存 468
         *
         * @param key   键 469
         * @param value 值 470
         * @return 472
         */
    
        public boolean lSet(String key, List<Object> value) {
            try {
                redisTemplate.opsForList().rightPushAll(key, value);
                return true;
            }
            catch (Exception e) {
                log.error("", e);
                return false;
            }
    
        }
    
        /**
         * 484 将list放入缓存 485
         * <p>
         * 486
         *
         * @param key   键 487
         * @param value 值 488
         * @param time  时间(秒) 489
         * @return 490
         */
    
        public boolean lSet(String key, List<Object> value, long time) {
    
            try {
    
                redisTemplate.opsForList().rightPushAll(key, value);
    
                if (time > 0) {
                    expire(key, time);
                }
                return true;
            }
            catch (Exception e) {
                log.error("", e);
                return false;
            }
    
        }
    
        /**
         * 根据索引修改list中的某条数据
         *
         * @param key   键
         * @param index 索引
         * @param value 值
         * @return
         */
    
        public boolean lUpdateIndex(String key, long index, Object value) {
    
            try {
                redisTemplate.opsForList().set(key, index, value);
                return true;
            }
            catch (Exception e) {
                log.error("", e);
                return false;
    
            }
    
        }
    
        /**
         * 移除N个值为value
         *
         * @param key   键
         * @param count 移除多少个
         * @param value 值
         * @return 移除的个数
         */
    
        public long lRemove(String key, long count, Object value) {
            try {
                Long remove = redisTemplate.opsForList().remove(key, count, value);
                return remove;
            }
            catch (Exception e) {
                log.error("", e);
                return 0;
    
            }
    
        }
    }

    注解使用:方法上添加注解即可,即5秒内同一ip最多调用5次

    @ApiAccessLimit(seconds = 5, maxCount = 5)
  • 相关阅读:
    《做衣服:破坏时尚》总结
    《程序员的思维修炼》总结
    纸玫瑰和鲜玫瑰,选择哪个?
    《古怪的身体:时尚是什么》总结
    《世界尽头的咖啡馆》总结
    《软技能:代码之外的生存指南》总结
    构造无限级树的框架套路,附上python/golang/php/js实现
    《Dior的时尚笔记》总结
    《编写可读代码的艺术》总结
    《费曼学习法》总结
  • 原文地址:https://www.cnblogs.com/sulishihupan/p/15384774.html
Copyright © 2020-2023  润新知