1. 添加maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 创建注解类AccessLimit
package com.KSF.annotation;
import java.lang.annotation.*;
/**
* <p>
* 注解 访问限制
* </p>
*
* @author KSF
* @since 2022/1/10 14:03
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface AccessLimit {
/**
* 访问时间限制
*
* @return int
* @author KSF
* @since 2022/1/10 14:07
*/
Seconds seconds();
/**
* 访问次数
*
* @return int
* @author KSF
* @since 2022/1/10 14:06
*/
MaxCount maxCount();
/**
* 缓存时间 枚举
*/
enum Seconds {
/**
* 5秒缓存时间
*/
FIVE(5);
private final Integer key;
private Seconds(Integer key) {
this.key = key;
}
public Integer getKey() {
return this.key;
}
}
/**
* 最大请求次数 枚举
*/
enum MaxCount {
/**
* 1次最大请求次数
*/
ONE(1);
private final Integer key;
private MaxCount(Integer key) {
this.key = key;
}
public Integer getKey() {
return this.key;
}
}
}
3. 创建拦截器SessionInterceptor
package com.KSF.Aspect;
import com.alibaba.fastjson.JSON;
import com.KSF.annotation.AccessLimit;
import com.KSF.vo.WrapperResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.concurrent.TimeUnit;
/**
* <p>
* 访问拦截器
* </p>
*
* @author KSF
* @since 2022/1/10 14:15
*/
@Slf4j
@Component
public class SessionInterceptor extends HandlerInterceptorAdapter {
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(handler instanceof HandlerMethod){
String unknown = "unknown";
HandlerMethod hm = (HandlerMethod) handler;
//获取方法中的注解,看是否有该注解
AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
if (null != accessLimit) {
int seconds = accessLimit.seconds().getKey();
int maxCount = accessLimit.maxCount().getKey();
//从redis中获取用户访问的次数
// 有可能ip是代理的
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.getRemoteAddr();
}
String key = ip + hm.getMethod().getName();
Integer count = (Integer) redisTemplate.opsForValue().get(key);
if (count == null) {
//第一次访问
redisTemplate.opsForValue().set(key, 1, seconds, TimeUnit.SECONDS);
} else if (count < maxCount) {
//加1
count = count + 1;
redisTemplate.opsForValue().set(key, count);
} else {
//超出访问次数
log.error("请求访问过快 【ip => " + ip + "】-【请求方法 => "+ hm.getMethod().getName() +"】 且在 " + seconds + " 秒内超过最大限制 => " + maxCount);
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Type", "application/json; charset=utf-8");
try (PrintWriter printWriter = response.getWriter()) {
printWriter.write(JSON.toJSONString(WrapperResponse.error(500, "操作太快了", "")));
}
return false;
}
}
}
return super.preHandle(request, response, handler);
}
}
拦截器用到的通用返回实体
package com.KSF.vo;
import com.KSF.constant.ResponseType;
import lombok.Data;
import java.io.Serializable;
/**
* 通用返回实体
*
* @author KSF
*/
@Data
public class WrapperResponse<T> implements Serializable {
/**
* 返回编码
*/
private int code;
/**
* 返回消息类型
*/
private String type;
/**
* 请求状态说明
*/
private String message = "";
/**
* 数据
*/
private T data;
/**
* success WrapperResponse
*
* @param data 数据对象
* @param <T> 数据类型
* @return WrapperResponse<T>
*/
public static <T> WrapperResponse<T> success(T data) {
return handlerWrapperResponse(0, "", data, ResponseType.SUCCESS.getType());
}
/**
* info WrapperResponse
*
* @param data 数据对象
* @param <T> 数据类型
* @return WrapperResponse<T>
*/
public static <T> WrapperResponse<T> info(int code, String message, T data) {
return handlerWrapperResponse(code, message, data, ResponseType.INFO.getType());
}
/**
* warning WrapperResponse
*
* @param data 数据对象
* @param <T> 数据类型
* @return WrapperResponse<T>
*/
public static <T> WrapperResponse<T> warning(int code, String message, T data) {
return handlerWrapperResponse(code, message, data, ResponseType.WARNING.getType());
}
/**
* error WrapperResponse
*
* @param <T> 数据类型
* @param code
* @param data 数据对象
* @return WrapperResponse<T>
*/
public static <T> WrapperResponse<T> error(int code, String message, T data) {
return handlerWrapperResponse(code, message, data, ResponseType.ERROR.getType());
}
/**
* 自定义固定错误 异常类型
* */
public static <T> WrapperResponse<T> errorProne(String message) {
return handlerWrapperResponse(-1, message, null, ResponseType.ERROR.getType());
}
private static <T> WrapperResponse<T> handlerWrapperResponse(int code, String message, T data, String responseType) {
WrapperResponse<T> result = new WrapperResponse<>();
result.setCode(code);
result.setData(data);
result.setMessage(message);
result.setType(responseType);
return result;
}
}
返回实体用到的返回类型
package com.KSF.constant;
/**
* 返回类型
*
* @author KSF
*/
public enum ResponseType {
/**
* info
*/
INFO("info"),
/**
* success
*/
SUCCESS("success"),
/**
* warning
*/
WARNING("warning"),
/**
* error
*/
ERROR("error");
private String type;
ResponseType(String type) {
this.type = type;
}
public String getType() {
return type;
}
}
4. 在项目中的WebMvcConfig配置中添加拦截器
package com.KSF.confg;
import com.KSF.Aspect.SessionInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Resource
private SessionInterceptor interceptor;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.setCachePeriod(31536000);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor);
}
}
5. 在API接口上添加上刚刚的自定义注解
@GetMapping("/getUserInfo")
@AccessLimit(seconds = AccessLimit.Seconds.FIVE, maxCount = AccessLimit.MaxCount.ONE)
public String getUserInfo() {
return "Welcome to Guang Zhou";
}
评论区