在SpringBoot定义一个方法注解,使用aop技术实现接口ip白名单拦截和接口验签功能

1.定义一个方法注解

/**
 * 自定义注解
 * @LMJ
 * DATE:2023/11/17
 */
@Target({ElementType.METHOD}) //在方法上运行
@Retention(RetentionPolicy.RUNTIME)
public @interface LmjApiFilter{             
    String[] whiteIpList();
}

@interface:是指声明一个注解,方法名对应参数名,返回值类型对应参数类型。

@Target: 是指自定义注解的使用位置

@Retention:是指于自定义注解的生命周期

RetentionPolicy:的取值包含以下三种:

  • SOURCE:源码级别保留,编译后即丢弃。

  • CLASS:编译级别保留,编译后的class文件中存在,在jvm运行时丢弃,这是默认值。

  • RUNTIME:运行级别保留,编译后的class文件中存在,在jvm运行时保留,可以被反射调用。

我们需要使用到AOP,所以选择最后一个!

2.编写拦截器

import com.sz.annotate.LmjApiFilter;
import org.json.JSONObject;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
​
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
​
/**
 * 拦截器[API白名单]
 * @LMJ
 * DATE:2023/11/17
 */
@Component
public class IpInterceptor implements HandlerInterceptor {
    private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(IpInterceptor.class);
​
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1.获取自定义注解
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        //注:验证注解是否正确
        LmjApiFilter ipWhiteList = AnnotationUtils.findAnnotation(handlerMethod.getMethod(),LmjApiFilter);
        if (ipWhiteList == null) {
            return true;
        }
        // 2.获取客户端真实ip地址
        String clientIp = getClientIpAddr(request);
        // 3.校验ip地址
        String[] whiteIpList = ipWhiteList.whiteIpList();
        for (String ip : whiteIpList) {
            if (ip.equals(clientIp)) {
                return true;
            }
        }
        fallback(clientIp, response);
        return false;
    }
​
    private void fallback(String clientIp, HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        PrintWriter writer = null;
        try {
            String message = String.format("您的IP地址为[%s],已被系统禁止访问,请联系管理员处理!", clientIp);
            Map<String,String> map = new HashMap();
            map.put("403",message);
            JSONObject json = new JSONObject(map);
            writer = response.getWriter();
            writer.append(json.toString());
        } catch (IOException e) {
            log.error(e);
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }
    private String getClientIpAddr(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.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
        if (ip != null && ip.length() > 15) { // "***.***.***.***".length() = 15
            if (ip.indexOf(",") > 0) {
                ip = ip.substring(0, ip.indexOf(","));
            }
        }
        return ip;
    }
}
拦截器重点:

验证请求中注解是否存在我们定义的注解,如果发现有我们定义的注解才走下面的逻辑。

拦截ip的逻辑也很简单

我们只需要判断访问主机的ip是不是我们拉入黑名单/白名单的ip。

黑名单的ip如何获取?
2.1.数据库中【redis/mysql】

2.2.【LmjApiFilter】定义一个属性

3:注解的使用

4:最后注册拦截器

package com.sz.config.ip;
​
import com.sz.interceptor.whiteIp.IpInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
​
@Configuration
public class IPWebMvcConfiguration implements WebMvcConfigurer {
​
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // registry 拦截器注册中心
        registry.addInterceptor(new IpInterceptor());
    }
​
}

逻辑解析

简单的来说,该方法目的就是实现黑名单功能,例如我把你拉入黑名单你就无法访问我们系统的接口了,只是加上注解后可以更灵活使用了,可以将某个接口单独提供给某个ip访问,或则不让某个ip访问!

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的,为了实现 Spring Boot 接口防刷的切面和注解,您需要执行以下步骤: 1. 在您的 Spring Boot 应用中创建一个切面类。在该类中,您可以使用 `@Aspect` 注解声明它是一个切面。 2. 在切面类中创建一个方法使用 `@Around` 注解声明它是一个环绕通知方法。环绕通知方法可以在目标方法执行前后执行指定的代码。 3. 在环绕通知方法中,使用 `@Pointcut` 注解声明切点,并使用 `ProceedingJoinPoint` 类的 `proceed()` 方法执行目标方法。 4. 在环绕通知方法中,使用注解来声明需要进行防刷的方法。您可以自定义注解,并使用 `@Target` 和 `@Retention` 注解来声明该注解可以用在方法上。 5. 在环绕通知方法中,使用注解中的参数来控制方法的访问频率。您可以使用任意方式来实现这一点,例如使用缓存或者计数器。 以下是一个简单的例子,该例子使用注解 `@AntiBrush` 来声明需要进行防刷的方法,并使用了缓存来实现防刷功能: ``` @Aspect @Component public class AntiBrushAspect { private Cache<String, AtomicInteger> cache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build ### 回答2: 首先,我们需要定义一个自定义注解,用于标识某个接口需要进行防刷限制。我们可以定义一个名为AntiSpam的注解,代码如下: ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AntiSpam { int limit() default 10; // 默认限制为10秒内只能请求10次 } ``` 接下来,我们需要实现一个切面来拦截带有AntiSpam注解接口。首先,需要引入以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> ``` 然后,在你的Spring Boot应用程序中创建一个名为AntiSpamAspect的类,代码如下: ```java @Aspect @Component public class AntiSpamAspect { private ConcurrentHashMap<String, Long> methodLastAccessTimeMap = new ConcurrentHashMap<>(); @Around("@annotation(antiSpam)") public Object handleAntiSpam(ProceedingJoinPoint joinPoint, AntiSpam antiSpam) throws Throwable { String methodName = joinPoint.getSignature().toShortString(); Long lastAccessTime = methodLastAccessTimeMap.get(methodName); long currentTime = System.currentTimeMillis(); if (lastAccessTime != null && currentTime - lastAccessTime < (antiSpam.limit() * 1000)) { throw new RuntimeException("操作频率过高,请稍后再试!"); } else { methodLastAccessTimeMap.put(methodName, currentTime); return joinPoint.proceed(); } } } ``` 在上面的代码中,我们使用了ConcurrentHashMap来存储每个方法最后一次访问的时间。在处理切面时,我们会根据方法名从map中获取上一次访问时间,并与当前时间进行比较。如果两次访问间隔小于限定时间,则抛出异常,否则继续执行方法。 最后,我们需要在Spring Boot的主类上添加@EnableAspectJAutoProxy注解,开启切面的自动代理功能。 至此,我们已经完成了Spring Boot接口防刷的切面和注解实现。接下来,只需要在需要进行防刷的接口方法上添加@AntiSpam注解,并在需要处理切面的类上添加@Component注解即可。 ### 回答3: 在Spring Boot实现接口防刷功能,可以通过切面和注解的方式来实现。 首先,我们需要定义一个自定义注解,用于标识需要进行接口防刷限制的方法。可以命名为@RateLimit。 ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RateLimit { int value(); // 设置接口频率限制值,比如每秒允许访问的次数 int timeout() default 1; // 设置超时时间,默认1秒 } ``` 接下来,我们可以定义一个切面类,用于处理接口防刷逻辑。可以命名为RateLimitAspect。 ```java @Aspect @Component public class RateLimitAspect { private Map<String, Long> requestCounts = new ConcurrentHashMap<>(); // 记录请求次数的Map @Around("@annotation(rateLimit)") public Object around(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable { String methodName = joinPoint.getSignature().toLongString(); int limit = rateLimit.value(); int timeout = rateLimit.timeout(); long currentTime = System.currentTimeMillis(); if (!requestCounts.containsKey(methodName)) { requestCounts.put(methodName, currentTime); return joinPoint.proceed(); } long lastTime = requestCounts.get(methodName); long interval = currentTime - lastTime; if (interval < timeout * 1000) { if (requestCounts.get(methodName + "_count") == null) { requestCounts.put(methodName + "_count", 1L); } else { long count = requestCounts.get(methodName + "_count"); if (count >= limit) { throw new RuntimeException("接口调用频率过高,请稍后再试!"); } requestCounts.put(methodName + "_count", count + 1); } } else { requestCounts.remove(methodName); requestCounts.remove(methodName + "_count"); } requestCounts.put(methodName, currentTime); return joinPoint.proceed(); } } ``` 在切面类中,我们使用了一个Map来记录接口每次请求的时间和次数。如果接口调用频率超过限制,则阻止请求继续执行,并抛出异常。 使用@Around注解和@RateLimit注解来标识切面和需要进行限制的方法。通过@Around注解,我们可以在接口方法的执行前后进行处理,从而实现防刷逻辑。 最后,我们需要在Spring Boot的启动类上添加@EnableAspectJAutoProxy注解,开启切面自动代理功能。 ```java @SpringBootApplication @EnableAspectJAutoProxy public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 这样,就可以在需要进行接口防刷限制的方法上添加@RateLimit注解,并在超过限制的情况下阻止请求继续执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值