SpringBoot+redis实现接口防刷
写一个RedisService,实现获取Redis 的set、get、incr(相当于计数器)
写@inferface注解类
做一个拦截器,因为要先于控制器判断
将拦截器注入Springboot
文章目录
目录
文章目录
前言
一、引入依赖
二、使用步骤
2.1 RedisService操作redis
2.2 防刷的自定义注解
2.3 自定义的过滤器
2.4 测试的控制类
2.5 测试结果
总结
前言
一、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
application.properties
spring.redis.port=6379
spring.redis.host=127.0.0.1
spring.redis.password=123456
二、使用步骤
2.1 RedisService操作redis
@Service
public class RedisService {
private final StringRedisTemplate stringRedisTemplate;
@Autowired
public RedisService(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
public void set(String key, String value) {
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
operations.set(key, value, 1, TimeUnit.MINUTES); // 设置值时设置过期时间为1分钟
}
public void set(String key, String value,int N) {
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
operations.set(key, value, N, TimeUnit.MINUTES); // 设置值时设置过期时间为N分钟
}
public String get(String key) {
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
return operations.get(key);
}
public Long incr(String key, long delta) {
return performIncrement(key, delta);
}
public Long incr(String key) {
return performIncrement(key, 1); // 使用默认增量 1
}
private Long performIncrement(String key, long delta) {
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
// 检查键是否存在,如果不存在,则设置初始值为 0 并设置过期时间
if (Boolean.FALSE.equals(stringRedisTemplate.hasKey(key))) {
operations.set(key, "0", 1, TimeUnit.MINUTES); // 设置过期时间为 1 分钟
}
return operations.increment(key, delta);
}
}
2.2 防刷的自定义注解
@Retention(RUNTIME)
@Target(METHOD)
public @interface AccessLimit {
//每分钟
int minutes();
//访问的次数限制
int maxCount();
//是否需要登录
boolean needLogin()default true;
}
2.3 自定义的过滤器
过滤器主要是访问的时候 处理加了防刷注解的接口 然后限制访问给出不一样的返回结果
@Component
public class PreventRefreshInterceptor extends HandlerInterceptorAdapter {
@Autowired
private RedisService redisService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("进入拦截器");
//判断请求是否属于方法的请求
if(handler instanceof HandlerMethod){
System.out.println("判断请求是否属于方法的请求");
HandlerMethod hm = (HandlerMethod) handler;
//获取方法中的注解,看是否有该注解
AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
if(accessLimit == null){
return true;
}
//@AccessLimit(seconds=5, maxCount=5, needLogin=true)
int minutes = accessLimit.minutes();
int maxCount = accessLimit.maxCount();
boolean login = accessLimit.needLogin();
Map<String, Object> mp=new HashMap<>();
mp.put("seconds",minutes);
mp.put("maxCount",maxCount);
mp.put("login",login);
System.out.println(mp);
String key = IPUtil.getIpAddr(request)+"_"+request.getRequestURI();
log.error("Key:{}",key);
//如果需要登录
// if(login){
// redisService.set(key, "1");
// }
//从redis中获取用户访问的次数
String value = redisService.get(key);
System.out.println("value:"+value);
//判断value是否为null
if(value == null){
System.out.println("第1次访问,设置有效期"+minutes +"分钟");
redisService.set(key, "1",minutes);
}else{
Long count = redisService.incr(key);
System.out.println("第"+count+"次访问,设置有效期"+minutes +"分钟");
System.out.println("{count :"+count + ", maxCount :"+maxCount+"}");
if(count.intValue() > maxCount){
System.out.println("超出访问次数");
redisService.set(key, "超出次数");
renderError(response);
}
}
}
return true;
}
private void renderError(HttpServletResponse response)throws Exception {
response.setContentType("application/json;charset=UTF-8");
OutputStream out = response.getOutputStream();
String str = JSON.toJSONString(ResultT.error("error","超出访问次数"));
out.write(str.getBytes("UTF-8"));
out.flush();
out.close();
}
}
把修改后的过滤器给spring mvc的web过滤器
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
private PreventRefreshInterceptor interceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
System.out.println("进入拦截器配置类");
registry.addInterceptor(interceptor);
}
}
2.4 测试的控制类
@RestController
public class RefreshController {
@AccessLimit(minutes=2, maxCount=10, needLogin=true)
@RequestMapping("/shua")
@ResponseBody
public ResultT shua(){
return ResultT.ok("请求成功");
}
}
统一结果类
public class ResultT extends HashMap<String, Object> {
public ResultT() {
put("code", 0);
put("msg", "success");
}
public static ResultT ok() {
ResultT t = new ResultT();
t.put("msg", "操作成功");
return t;
}
public static ResultT ok(String msg) {
ResultT t = new ResultT();
t.put("msg", msg);
return t;
}
public static ResultT ok(Map<String, Object> map) {
ResultT t = new ResultT();
map.putAll(t);
return t;
}
public static ResultT error(String code,String msg) {
ResultT t = new ResultT();
t.put("code", code);
t.put("msg", msg);
return t;
}
public static ResultT error(String msg) {
return error("500", msg);
}
public ResultT put(String key, Object value){
super.put(key, value);
return this;
}
}
public class IPUtil {
private static final String UNKNOWN = "unknown";
protected IPUtil() {
}
/**
* 获取 IP地址
* 使用 Nginx等反向代理软件, 则不能通过 request.getRemoteAddr()获取 IP地址
* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,
* X-Forwarded-For中第一个非 unknown的有效IP字符串,则为真实IP地址
*/
public static String getIpAddr(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.getRemoteAddr();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
}
}
2.5 测试结果