当前位置: 首页 > article >正文

SpringBoot+redis实现接口防刷

  1. 写一个RedisService,实现获取Redis 的set、get、incr(相当于计数器)

  2. 写@inferface注解类

  3. 做一个拦截器,因为要先于控制器判断

  4. 将拦截器注入Springboot

文章目录

目录

文章目录

前言

一、引入依赖

二、使用步骤

2.1 RedisService操作redis

2.2 防刷的自定义注解

2.3 自定义的过滤器

2.4 测试的控制类 

2.5 测试结果 

总结



前言


一、引入依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
 <groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>

application.properties

spring.redis.port=6379
spring.redis.host=127.0.0.1
spring.redis.password=123456

二、使用步骤

2.1 RedisService操作redis

@Service
public class RedisService {
    private final StringRedisTemplate stringRedisTemplate;

    @Autowired
    public RedisService(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    public void set(String key, String value) {
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
        operations.set(key, value, 1, TimeUnit.MINUTES); // 设置值时设置过期时间为1分钟
    }

    public void set(String key, String value,int N) {
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
        operations.set(key, value, N, TimeUnit.MINUTES); // 设置值时设置过期时间为N分钟
    }

    public String get(String key) {
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
        return operations.get(key);
    }

    public Long incr(String key, long delta) {
        return performIncrement(key, delta);
    }

    public Long incr(String key) {
        return performIncrement(key, 1); // 使用默认增量 1
    }

    private Long performIncrement(String key, long delta) {
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();

        // 检查键是否存在,如果不存在,则设置初始值为 0 并设置过期时间
        if (Boolean.FALSE.equals(stringRedisTemplate.hasKey(key))) {
            operations.set(key, "0", 1, TimeUnit.MINUTES); // 设置过期时间为 1 分钟
        }

        return operations.increment(key, delta);
    }
}

2.2 防刷的自定义注解

@Retention(RUNTIME)
@Target(METHOD)
public @interface AccessLimit {

    //每分钟
    int minutes();
    //访问的次数限制
    int maxCount();
//是否需要登录
    boolean needLogin()default true;
}

 

2.3 自定义的过滤器

过滤器主要是访问的时候 处理加了防刷注解的接口 然后限制访问给出不一样的返回结果

@Component
public class PreventRefreshInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private RedisService redisService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("进入拦截器");
        //判断请求是否属于方法的请求
        if(handler instanceof HandlerMethod){
            System.out.println("判断请求是否属于方法的请求");
            HandlerMethod hm = (HandlerMethod) handler;
            //获取方法中的注解,看是否有该注解
            AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
            if(accessLimit == null){
                return true;
            }
            //@AccessLimit(seconds=5, maxCount=5, needLogin=true)
            int minutes = accessLimit.minutes();
            int maxCount = accessLimit.maxCount();
            boolean login = accessLimit.needLogin();
            Map<String, Object> mp=new HashMap<>();
            mp.put("seconds",minutes);
            mp.put("maxCount",maxCount);
            mp.put("login",login);
            System.out.println(mp);
            String key = IPUtil.getIpAddr(request)+"_"+request.getRequestURI();
            log.error("Key:{}",key);
            //如果需要登录
//            if(login){
//                redisService.set(key, "1");
//            }
            //从redis中获取用户访问的次数
            String value = redisService.get(key);
            System.out.println("value:"+value);
            //判断value是否为null
            if(value == null){
                System.out.println("第1次访问,设置有效期"+minutes +"分钟");
                redisService.set(key, "1",minutes);
            }else{
                Long count = redisService.incr(key);
                System.out.println("第"+count+"次访问,设置有效期"+minutes +"分钟");
                System.out.println("{count :"+count + ", maxCount :"+maxCount+"}");
                if(count.intValue() > maxCount){
                    System.out.println("超出访问次数");
                    redisService.set(key, "超出次数");
                    renderError(response);
                }
            }
        }

        return true;

    }
    private void renderError(HttpServletResponse response)throws Exception {
        response.setContentType("application/json;charset=UTF-8");
        OutputStream out = response.getOutputStream();
        String str  = JSON.toJSONString(ResultT.error("error","超出访问次数"));
        out.write(str.getBytes("UTF-8"));
        out.flush();
        out.close();

    }
}

把修改后的过滤器给spring mvc的web过滤器

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Autowired
    private PreventRefreshInterceptor interceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        System.out.println("进入拦截器配置类");
        registry.addInterceptor(interceptor);
    }
}

2.4 测试的控制类 

@RestController
public class RefreshController {

    @AccessLimit(minutes=2, maxCount=10, needLogin=true)
    @RequestMapping("/shua")
    @ResponseBody
    public ResultT shua(){
        
        return ResultT.ok("请求成功");

    }

}

统一结果类

public class ResultT extends HashMap<String, Object> {
    public ResultT() {
        put("code", 0);
        put("msg", "success");
    }

    public static ResultT ok() {
        ResultT t = new ResultT();
        t.put("msg", "操作成功");
        return t;
    }

    public static ResultT ok(String msg) {
        ResultT t = new ResultT();
        t.put("msg", msg);
        return t;
    }

    public static ResultT ok(Map<String, Object> map) {
        ResultT t = new ResultT();
        map.putAll(t);
        return t;
    }

    public static ResultT error(String code,String msg) {

        ResultT t = new ResultT();
        t.put("code", code);
        t.put("msg", msg);
        return t;
    }

    public static ResultT error(String msg) {
        return error("500", msg);
    }
    public ResultT put(String key, Object value){
        super.put(key, value);
        return this;
    }
}

public class IPUtil {

    private static final String UNKNOWN = "unknown";

    protected IPUtil() {

    }

    /**
     * 获取 IP地址
     * 使用 Nginx等反向代理软件, 则不能通过 request.getRemoteAddr()获取 IP地址
     * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,
     * X-Forwarded-For中第一个非 unknown的有效IP字符串,则为真实IP地址
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }

}

2.5 测试结果 

 


总结


http://www.kler.cn/a/154209.html

相关文章:

  • 【机器学习】聚类算法原理详解
  • 分布式cap理论学习
  • 【AlphaFold3】开源本地的安装及使用
  • 无人机动力系统测试-实测数据与CFD模拟仿真数据关联对比分析
  • 使用Web Animations API实现复杂的网页动画效果
  • django解决跨域问题
  • Web前端 ---- 【Vue】(组件)父子组件之间的通信一文带你了解
  • 【C语言:数据在内存中的存储】
  • xilinx系列FPGA基于VIVADO的pin delay列表生成说明
  • 2017年全国硕士研究生入学统一考试管理类专业学位联考英语(二)试题
  • 基于helm的方式在k8s集群中部署gitlab - 备份恢复(二)
  • 【Java Spring】SpringBoot 配置文件
  • 速通MySql
  • 熬夜会秃头——beta冲刺Day4
  • MySQL备份与恢复(重点)
  • Golang笔记|Atomic
  • 【一周安全资讯1202】信安标委发布《网络安全标准实践指南—网络安全产品互联互通 告警信息格式》;网络安全纳入注册会计师考试科目
  • Android 手机屏幕适配方式和原理
  • (一)Tiki-taka算法(TTA)求解无人机三维路径规划研究(MATLAB)
  • 【UGUI】Unity为下拉菜单添加选项(DropDown)
  • SQL Server对象类型(7)——4.7.触发器(Trigger)
  • 自定义类型-结构体,联合体和枚举-C语言
  • matlab 无迹卡尔曼滤波
  • 力扣129. 求根节点到叶节点数字之和
  • Git自动化利器:使用Node.js脚本定制化提交消息处理
  • 电大搜题微信公众号详解,助力学习者轻松考试