🥫🍞

短信接口防盗用

2022-07-27

  1. 校验 Referer 头部
  2. 签名校验
  3. mobile 参数可以略微进行 AES 加密等,增加复杂度
  4. IP + UA 限制次数
    例如,使用 Redis 记录次数

假设 key 特定前缀是 code_send_limit_

key = code_send_limit_ + <IP 和 UA 的混合>
value = 次数

incr expireAt

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public class FrequencyInterceptor implements HandlerInterceptor {

/**
* 最大限制
*/
private final int maxLimit;

/**
* 时间段
*/
private final int periodInSeconds;


RedisTemplate<String, Object> redisTemplate;

public FrequencyInterceptor(int maxLimit, int periodInSeconds, RedisTemplate<String, Object> redisTemplate) {
this.maxLimit = maxLimit;
this.periodInSeconds = periodInSeconds;
this.redisTemplate = redisTemplate;
}


@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String ipAddr = IpUtil.getIpAddr(request);
String userAgent = request.getHeader("user-agent");

MessageDigest md5 = MessageDigest.getInstance("md5");
byte[] digest = md5.digest((ipAddr + userAgent).getBytes(StandardCharsets.UTF_8));
final String ipAndUa = new BigInteger(1, md5.digest()).toString(16);


String key = "send_msg_limit:" + ipAndUa;
Integer limit = (Integer) redisTemplate.opsForValue().get(key);
if (limit == null) {
// 此处可能高并发
Long newValue = redisTemplate.opsForValue().increment(key);
if (newValue == null) {
// 出错
throw new RuntimeException("网络繁忙,请稍后重试");
}
// 只需考虑第一个设置过期时间
if (newValue == 1) {
redisTemplate.expire(key, Duration.ofSeconds(periodInSeconds));
return true;
} else if (newValue <= maxLimit) {
return true;
} else {
// 如果是溢出请求,全部打回
throw new RuntimeException("操作频繁,请稍后重试");
}
} else if (limit > 5) {
// 一般情况,溢出打回
throw new RuntimeException("操作频繁,请稍后重试");
} else {
Long newValue = redisTemplate.opsForValue().increment(key);
if (newValue == null) {
throw new RuntimeException("网络繁忙,请稍后重试");
}
if (newValue <= maxLimit) {
return true;
} else {
throw new RuntimeException("操作频繁,请稍后重试");
}
}
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...");
}


@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...");
}
}
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章