当前位置: 代码网 > it编程>编程语言>Java > springboot接口服务,防刷、防止请求攻击,AOP实现方式

springboot接口服务,防刷、防止请求攻击,AOP实现方式

2024年11月25日 Java 我要评论
springboot接口服务,防刷、防止请求攻击,aop实现本文使用aop的方式防止spring boot的接口服务被网络攻击pom.xml 中加入 aop 依赖<dependency>

springboot接口服务,防刷、防止请求攻击,aop实现

本文使用aop的方式防止spring boot的接口服务被网络攻击

pom.xml 中加入 aop 依赖

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-aop</artifactid>
</dependency>

aop自定义注解类

package org.jeecg.common.aspect.annotation;

import java.lang.annotation.*;

/**
 * 用于防刷限流的注解
 *      默认是5秒内只能调用一次
 */
@target({ elementtype.method })
@retention(retentionpolicy.runtime)
@documented
public @interface ratelimit {

    /** 限流的key */
    string key() default "limit:";

    /** 周期,单位是秒 */
    int cycle() default 5;

    /** 请求次数 */
    int count() default 1;

    /** 默认提示信息 */
    string msg() default "请勿重复点击";
}

aop切面业务类

package org.jeecg.common.aspect;

import org.aspectj.lang.proceedingjoinpoint;
import org.aspectj.lang.annotation.around;
import org.aspectj.lang.annotation.aspect;
import org.aspectj.lang.annotation.pointcut;
import org.aspectj.lang.reflect.methodsignature;
import org.jeecg.common.aspect.annotation.ratelimit;
import org.springframework.aop.aspectj.methodinvocationproceedingjoinpoint;
import org.springframework.data.redis.core.redistemplate;
import org.springframework.stereotype.component;
import org.springframework.web.context.request.requestcontextholder;
import org.springframework.web.context.request.servletrequestattributes;
import javax.annotation.resource;
import javax.servlet.http.httpservletrequest;
import java.lang.reflect.method;
import java.util.concurrent.timeunit;

/**
 * 切面类:实现限流校验
 */
@aspect
@component
public class accesslimitaspect {

    @resource
    private redistemplate<string, integer> redistemplate;

    /**
     * 这里我们使用注解的形式
     * 当然,我们也可以通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method
     */
    @pointcut("@annotation(org.jeecg.common.aspect.annotation.ratelimit)")
    public void limitpointcut() {
    }

    /**
     * 环绕通知
     */
    @around("limitpointcut()")
    public object around(proceedingjoinpoint pjp) throws throwable {
        // 获取被注解的方法
        methodinvocationproceedingjoinpoint mjp = (methodinvocationproceedingjoinpoint) pjp;
        methodsignature signature = (methodsignature) mjp.getsignature();
        method method = signature.getmethod();

        // 获取方法上的注解
        ratelimit ratelimit = method.getannotation(ratelimit.class);
        if (ratelimit == null) {
            // 如果没有注解,则继续调用,不做任何处理
            return pjp.proceed();
        }
        /**
         * 代码走到这里,说明有 ratelimit 注解,那么就需要做限流校验了
         *  1、这里可以使用redis的api做计数校验
         *  2、这里也可以使用lua脚本做计数校验,都可以
         */
        //获取request对象
        servletrequestattributes attributes = (servletrequestattributes) requestcontextholder.getrequestattributes();
        httpservletrequest request = attributes.getrequest();
        // 获取请求ip地址
        string ip = getipaddr(request);
        // 请求url路径
        string uri = request.getrequesturi();
        //存到redis中的key
        string key = "ratelimit:" + ip + ":" + uri;
        // 缓存中存在key,在限定访问周期内已经调用过当前接口
        if (redistemplate.haskey(key)) {
            // 访问次数自增1
            redistemplate.opsforvalue().increment(key, 1);
            // 超出访问次数限制
            if (redistemplate.opsforvalue().get(key) > ratelimit.count()) {
                throw new runtimeexception(ratelimit.msg());
            }
            // 未超出访问次数限制,不进行任何操作,返回true
        } else {
            // 第一次设置数据,过期时间为注解确定的访问周期
            redistemplate.opsforvalue().set(key, 1, ratelimit.cycle(), timeunit.seconds);
        }
        return pjp.proceed();
    }

    //获取请求的归属ip地址
    private string getipaddr(httpservletrequest request) {
        string ipaddress = null;
        try {
            ipaddress = request.getheader("x-forwarded-for");
            if (ipaddress == null || ipaddress.length() == 0 || "unknown".equalsignorecase(ipaddress)) {
                ipaddress = request.getheader("proxy-client-ip");
            }
            if (ipaddress == null || ipaddress.length() == 0 || "unknown".equalsignorecase(ipaddress)) {
                ipaddress = request.getheader("wl-proxy-client-ip");
            }
            if (ipaddress == null || ipaddress.length() == 0 || "unknown".equalsignorecase(ipaddress)) {
                ipaddress = request.getremoteaddr();
            }
            // 对于通过多个代理的情况,第一个ip为客户端真实ip,多个ip按照','分割
            if (ipaddress != null && ipaddress.length() > 15) {
                // = 15
                if (ipaddress.indexof(",") > 0) {
                    ipaddress = ipaddress.substring(0, ipaddress.indexof(","));
                }
            }
        } catch (exception e) {
            ipaddress = "";
        }
        return ipaddress;
    }
}

测试

package org.jeecg.modules.api.controller;
import org.jeecg.common.api.vo.result;
import org.jeecg.common.aspect.annotation.ratelimit;
import org.springframework.web.bind.annotation.getmapping;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.restcontroller;

/**
 * 测试接口
 * @author wujiangbo
 * @date 2022-08-23 18:50
 */
@restcontroller
@requestmapping("/test")
public class testcontroller {

    //4秒内只能访问2次
    @ratelimit(key= "testlimit", count = 2, cycle = 4, msg = "大哥、慢点刷请求!")
    @getmapping("/test001")
    public result<?> rate() {
        system.out.println("请求成功");
        return result.ok("请求成功!");
    }
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com