如何使用redis实现接口防刷拦截器
文章目录分析准备工作RequestUtilRedisKeys管理枚举防刷拦截器拦截器调用的业务层实现方法配置拦截器分析避免同一个客户端短时间内重复访问一次接口过多次数此演示,一个客户端访问一个接口,1分钟内访问超过10次则被禁止访问,1分钟过后即可继续访问~准备工作RequestUtil获取ip地址的工具类public class RequestUtil {public static String
·
分析
- 避免同一个客户端短时间内重复访问一次接口过多次数
- 此演示,一个客户端访问一个接口,1分钟内访问超过10次则被禁止访问,1分钟过后即可继续访问~
准备工作
RequestUtil
- 获取ip地址的工具类
public class RequestUtil {
public static String getIPAddress() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); ;
String ip = null;
//X-Forwarded-For:Squid 服务代理
String ipAddresses = request.getHeader("X-Forwarded-For");
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//Proxy-Client-IP:apache 服务代理
ipAddresses = request.getHeader("Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//WL-Proxy-Client-IP:weblogic 服务代理
ipAddresses = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//HTTP_CLIENT_IP:有些代理服务器
ipAddresses = request.getHeader("HTTP_CLIENT_IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//X-Real-IP:nginx服务代理
ipAddresses = request.getHeader("X-Real-IP");
}
//有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP
if (ipAddresses != null && ipAddresses.length() != 0) {
ip = ipAddresses.split(",")[0];
}
//还是不能获取到,最后再通过request.getRemoteAddr();获取
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
RedisKeys管理枚举
- 设定此key的时效时长是一分钟,所以当用户访问同一个接口过多,则限制1分钟不能再访问
//redis的key的管理类
//约定: 一个redis key 映射一个枚举实例
@Getter
public enum RedisKeys {
BRUSH_PROOF("brush_proof",60L),
private String prefix;//key的前缀
private Long time;//key有效时间,单位是s
RedisKeys(String prefix, Long time) {
this.prefix = prefix;
this.time = time;
}
// 拼接出完整redis的key
public String join(String... values) {
StringBuilder sb = new StringBuilder(80);
sb.append(this.prefix);
for (String value : values) {
sb.append(":").append(value);
}
return sb.toString();
}
}
防刷拦截器
- 注入
ISecurityRedisService
接口,调用方法,操作redis - 将访问的接口和访问的客户端唯一的ip地址拼接作为redis的key,这样就能锁定一个客户端的操作次数
- 若此客户端
request.getRequestURI().substring(1);
获取访问的url,除去第一个字符/
RequestUtil.getIPAddress();
获取访问的客户端的ip地址
/**
* 防刷拦截器
*/
public class BrushProofInterceptor implements HandlerInterceptor {
@Autowired
private ISecurityRedisService securityRedisService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if(!(handler instanceof HandlerMethod)){
return true;
}
//防刷验证
String url = request.getRequestURI().substring(1);
String ip = RequestUtil.getIPAddress();
String key = RedisKeys.BRUSH_PROOF.join(url, ip);
if(!securityRedisService.isAllowBrush(key)){
response.setContentType("text/json;charset=UTF-8");
response.getWriter().write(JSON.toJSONString(JsonResult.error(500, "请勿频繁访问","谢谢咯")));
return false;
}
return true;
}
}
拦截器调用的业务层实现方法
- 先进行key的添加,value值为10 (若有此key,则不添加)
- 若同一个key参数传过来,每次执行此方法则将对应value值减1,执行10次以后则此key的value值小于0,返回false(不放行,禁止此客户端再次访问)
- 使用的
setIfAbsent
方法是重点,若有此key则不进行时效时长的刷新,还是继续时效性倒计时,时效性没了,又可以继续访问接口了
@Service
public class SecurityRedisServiceImpl implements ISecurityRedisService {
@Autowired
private StringRedisTemplate template;
@Override
public boolean isAllowBrush(String key) {
//如果有不做 任何操作,如果没有添加
template.opsForValue().setIfAbsent(key, "10", RedisKeys.BRUSH_PROOF.getTime(), TimeUnit.SECONDS);
Long decrement = template.opsForValue().decrement(key);
return decrement >= 0;//减一
}
}
配置拦截器
- 配置拦截器的配置类,要实现WebMvcConfigurer 接口
- 这里可配多种拦截器,此演示只演示配置此配置拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
//拦截器配置
@Bean
public BrushProofInterceptor brushProofInterceptor() {
return new BrushProofInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(brushProofInterceptor())
.addPathPatterns("/**");
}
}
更多推荐
已为社区贡献2条内容
所有评论(0)