java实现接口防刷(http接口防刷)

createh52个月前 (02-01)技术教程8

#头条创作挑战赛#

一、接口的安全性

  • 1、防伪装攻击
  • 处理方式:接口防刷
  • 出现的的情况:公共网络环境中,第三方有意或者恶意调用我们的接口
  • 2、防篡改攻击
  • 处理方式:签名机制
  • 出现情况:请求头/查询字符串/内容 在传输中来修改其内容
  • 3、防重放攻击
  • 处理方式:接口时效性
  • 出现情况:请求被截获,稍后被重放或多次重放
  • 4、防止止数据信息泄露
  • 处理方式:接口加密(对称加解密)
  • 出现情况:截获用户登录请求,主要是截获账号密码

二、实现自定义的注解

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;

@Retention(RetentionPolicy.RUNTIME)
@Target(METHOD)
@Documented
public @interface RateLimit {
    String cycle() default "5"; //请求等待的时间
    String number() default "1"; //短时间内多少次的请求
    String msg() default "请求繁忙,请稍后点击";
}

三、切面代码的实现

import com.south.wires.config.annotation.RateLimit;
import com.south.wires.result.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Collections;

@Slf4j
@Aspect
@Component
public class RateLimitAspect {

    @Autowired
    private RedisTemplate redisTemplate;
    
    @Around("@annotation(com.xxx.xxx.config.annotation.RateLimit)")
    public JsonResult around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 业务方法执行之前设置数据源...
        boolean pass=doingSomthingBefore(joinPoint);
        Object result;
        if(pass){
           // 执行业务方法
            result =joinPoint.proceed();
        }else{
            // 业务方法执行之后清除数据源设置...
            result=doingSomthingAfter(joinPoint);
        }
      //自定义的返回值的类型
        return JsonResult.success(result);
    }

    private boolean doingSomthingBefore(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String ip = request.getRemoteAddr();
        String uri = request.getRequestURI();
        // 记录下请求内容
        log.info("请求类型 :" + request.getMethod() + "  " + "请求URL : " + request.getRequestURL());
        log.info("请求IP  : " + request.getRemoteAddr());
        log.info("请求方法 : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        Method targetMethod=getTargetMethod(joinPoint);
        return selectLimit(ip, uri,targetMethod);
    }

    private Method getTargetMethod(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        //获取目标对象对应的字节码对象
        Class targetCls=joinPoint.getTarget().getClass();
        //获取方法签名信息从而获取方法名和参数类型
        Signature signature=joinPoint.getSignature();
        //将方法签名强转成MethodSignature类型,方便调用
        MethodSignature ms= (MethodSignature)signature;
        return targetCls.getDeclaredMethod(ms.getName(),ms.getParameterTypes());
    }

    private String doingSomthingAfter(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        System.out.println("开始后");
        //获取方法上的自定义RateLimit注解
        RateLimit rateLimit=getTargetMethod(joinPoint).getAnnotation(RateLimit.class);
        return rateLimit.msg();
    }

    private static final String SCRIPT = "local limit = tonumber(ARGV[1]);"// 限制次数
            + "local expire_time = ARGV[2];"// 过期时间
            + "local result = redis.call('setNX',KEYS[1],1);"// key不存在时设置value为1,返回1、否则返回0
            + "if result == 1 then"// 返回值为1,key不存在此时需要设置过期时间
            + "       redis.call('expire',KEYS[1],expire_time);"// 设置过期时间
            + "       return 1; "// 返回1
            + "else"// key存在
            + "       if tonumber(redis.call('GET', KEYS[1])) >= limit then"// 判断数目比对
            + "          return 0;"// 如果超出限制返回0
            + "       else" //
            + "          redis.call('incr', KEYS[1]);"// key自增
            + "          return 1 ;"// 返回1
            + "       end "// 结束
            + "end";// 结束

    public Boolean selectLimit(String ip, String url,Method targetMethod) {
        String key = "custom:rate" + ip + ":" + url;
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setValueSerializer(stringRedisSerializer);
        redisTemplate.setKeySerializer(stringRedisSerializer);
        DefaultRedisScript defaultRedisScript = new DefaultRedisScript<>(SCRIPT);
        defaultRedisScript.setResultType(Boolean.class);
        Boolean execute = null;
        try {
            RateLimit rateLimit=targetMethod.getAnnotation(RateLimit.class);
            execute = (Boolean) redisTemplate.execute(defaultRedisScript, Collections.singletonList(key), rateLimit.number(),rateLimit.cycle());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        if (!execute) {
            return false;
        }
        return true;
    }
}

四、控制层添加相应的注解

@RateLimit
@ResponseBody
@RequestMapping("testRateLimit")
public Object test2(){
    System.out.println("执行检索");
    return "请求成功";
}

相关文章

全面理解Java接口(java接口总结)

接口接口概念接口(Interface),在JAVA编程语言中是一个抽象类型,是一系列方法的声明,是一些方法特征的集合。 一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现...

说完 Java 的 Abstract 后再来说说接口 (interface )

如你对 Abstract 修饰的抽象类不是非常了解的话,请自行先考古下。这篇文章需要对 Java 定义过的抽象类有一些基本的了解才可以。抽象类和抽象方法用 Abstract 修饰的类,叫做抽象类,那么...

Java基础之浅谈接口(java接口基础知识)

前言前几篇文章我们已经把Java的封装、继承、多态学习完了,现在我们开始比较便于我们实际操作的学习,虽然它也是Java基础部分,但是其实入门容易,精通很难。我认真的给大家整理了一下这些必须学会、了解的...

Java的类与接口(java 接口和类)

Java是一门面向对象的编程语言,主要核心点就是类,Java类具有封装,继承,多态的特性;在Java中,类里面包含了某类事物的基本属性,将这些属性封装起来,只对外部公开别人可以访问的信息,不想让别人访...

今天就来随便讲讲:Java 接口和抽象类的区别吧。(详解)

在面向对象编程中,抽象类和接口是两个经常被用到的语法概念,是面向对象四大特性,以及很多设计模式、设计思想、设计原则编程实现的基础。下面就来讲讲二者的区别。什么是抽象类和接口? 区别在哪里?不同的编程语...

揭秘什么是面向接口编程(面向接口的好处)

先用一个案例来给大家说明一下面向接口编程。案例:有一个电脑类(Computer),电脑除了有基本的开机关机功能外,还有连接任何外接设备的功能,比如能电脑能连接外置键盘(Keyboard),鼠标(Mou...