Java限制接口访问

接口访问限制实现

目的

限制用户在一定时间内只能访问接口的次数。

后端限制方法

原理

1.通过 Redis 记录请求次数,如果超过限制则拒绝访问。同时设置 Redis 的 key 的时效性,过期自动销毁。

2.使用限流器

自定义注解(Java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 请求限制的自定义注解
*
* @Target 注解可修饰的对象范围:
* - ElementType.METHOD: 作用于方法
* - ElementType.TYPE: 作用于类
* @Retention 定义注解的生命周期:
* - RetentionPolicy.RUNTIME: 在运行时有效
* @Inherited 表示注解可被继承
*/
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface AccessLimit {
// 失效时间(秒)
int seconds();

// 最大请求次数
int maxCount();
}

配置拦截器(Java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@Component
public class LimitInterceptor implements HandlerInterceptor {
private Logger logger = LoggerFactory.getLogger(LimitInterceptor.class);

@Autowired
private RedisCache redisCache;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod hm = (HandlerMethod) handler;
AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
if (accessLimit == null) {
return true;
}

int seconds = accessLimit.seconds(); // 失效时间(秒)
int maxCount = accessLimit.maxCount(); // 最大请求次数
String key = request.getServletPath() + request.getSession().getId();

Integer count = 0;
if (redisCache.getCacheObject(key) != null) {
count = (Integer) redisCache.getCacheObject(key);
}

if (count == 0) {
// 第一次访问
redisCache.setCacheObject(key, 1);
redisCache.expire(key, seconds, TimeUnit.SECONDS); // 设置缓存失效时间
logger.info("{} 第一次访问", key);
} else if (count < maxCount) {
// 增加访问次数
redisCache.incr(key, 1);
} else {
// 超出访问次数
logger.error("超出访问次数");
render(response, "请求次数过于频繁!");
return false;
}
}
return true;
}

/**
* 封装返回值
*
* @param response 响应对象
* @param msg 返回消息
* @throws Exception 异常
*/
private void render(HttpServletResponse response, String msg) throws Exception {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.print(new ObjectMapper().writeValueAsString(RespBean.error(msg)));
out.flush();
out.close();
}
}

注册拦截器(Java)

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public LimitInterceptor limitHandlerInterceptor() {
return new LimitInterceptor();
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(limitHandlerInterceptor());
}
}

在 Controller 的接口加上注解(Java)

1
2
3
4
5
6
7
8
@PostMapping("/")
@AccessLimit(seconds = 6, maxCount = 1) // 用户重复访问该接口必须间隔 6 秒及以上
public RespBean addEmp(@RequestBody Employee employee) {
if (employeeService.addEmp(employee)) {
return RespBean.ok("添加成功!");
}
return RespBean.error("添加失败!");
}

Java限制接口访问
http://blog.hrseno.cn/2024/07/12/Java-限制接口访问/
作者
黄浩森
发布于
2024年7月12日
许可协议