用拦截器+注解+cookie进行简单限流访问案例


之前有一篇用aop+redis+lua进行限流的案例:
springboot中使用aop+redis+lua限流

思路是通过前端的cookie中携带的token解析出当前用户,每个用户进行线程隔离,访问次数存入Redis中
用拦截器也可以达到同样限流的目的,在这里同时使用了cookie,用于获取用户信息,代码如下:

1.写限流注解

@Retention(RUNTIME)
@Target(METHOD)
public @interface AccessLimit {
    
    
	int seconds();
	int maxCount();
	boolean needLogin() default true;
}

2.redis相关类

/**
 * @author hzy
 * @date 2021-12-04
 * 用于自定义键前缀和过期时间
 */
public class BasePrefix {
    
    
    private int expireSeconds;

    private String prefix;

    public BasePrefix(String prefix) {
    
    //0代表永不过期
        this(0, prefix);
    }

    public BasePrefix( int expireSeconds, String prefix) {
    
    
        this.expireSeconds = expireSeconds;
        this.prefix = prefix;
    }


    public int expireSeconds() {
    
    //默认0代表永不过期
        return expireSeconds;
    }


    public String getPrefix() {
    
    
        String className = getClass().getSimpleName();
        return className+":" + prefix;
    }
}

@Service
public class RedisServiceImpl implements RedisService {
    
    
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Override
    public void set(String key, Object value, long time) {
    
    
redisTemplate.opsForValue().set(key,value,time, TimeUnit.SECONDS);
    }

    @Override
    public <T> boolean set(String prefix, String key, T value) {
    
    
        {
    
    

            String str = JsonUtil.toJsonStr(value);
            if(str == null || str.length() <= 0) {
    
    
                return false;
            }
            //生成真正的key
            String realKey  = prefix+ key;
            redisTemplate.opsForValue().set(realKey,str);
            return true;
        }
    }

    @Override
    public void set(String key, Object value) {
    
    
redisTemplate.opsForValue().set(key,value);
    }

    @Override
    public Object get(String key) {
    
    
        return redisTemplate.opsForValue().get(key);
    }

    @Override
    public Boolean del(String key) {
    
    
        return redisTemplate.delete(key);
    }

    @Override
    public Long del(List<String> keys) {
    
    
        return redisTemplate.delete(keys);
    }

    @Override
    public Boolean expire(String key, long time) {
    
    
        return redisTemplate.expire(key,time,TimeUnit.SECONDS);
    }

    @Override
    public Long getExpire(String key) {
    
    
        return redisTemplate.getExpire(key,TimeUnit.SECONDS);
    }

    @Override
    public Boolean hasKey(String key) {
    
    
        return redisTemplate.hasKey(key);
    }

    @Override
    public Long incr(String key, long delta) {
    
    
        return redisTemplate.opsForValue().increment(key,delta);//增加
    }

    @Override
    public Long incr(String key) {
    
    
        return redisTemplate.opsForValue().increment(key);
    }

    @Override
    public Long decr(String key, long delta) {
    
    
        return redisTemplate.opsForValue().decrement(key,delta);
    }

 
//还有一些操作list set hash等的操作就省略了
}

3.添加自定义拦截器:

public class AccessInterceptor implements HandlerInterceptor {
    
    
    public static final String COOKI_NAME_TOKEN = "token";
    @Autowired
    IUserService userService;

    @Autowired
    RedisService redisService;
   
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
    
    
        if(handler instanceof HandlerMethod) {
    
    
           User user = getUser(request, response);
            ThreadlocalUser.setUser(user);//进行线程隔离
            //用
            HandlerMethod hm = (HandlerMethod)handler;
            AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
            if(accessLimit == null) {
    
    
                return true;
            }
            int seconds = accessLimit.seconds();
            int maxCount = accessLimit.maxCount();
            boolean needLogin = accessLimit.needLogin();
            String key = request.getRequestURI();
            if(needLogin) {
    
    
                if(user == null) {
    
    
                    render(response, "SESSION_ERROR");
                    return false;
                }
                key += "_" + user.getId();
            }else {
    
    
                //do nothing
            }
     //       AccessKey ak = AccessKey.withExpire(seconds);
            String ak = "access";
            Integer count = (Integer) redisService.get("access"+key);
            if(count  == null) {
    
    
                redisService.set(ak+key, 1);
            }else if(count < maxCount) {
    
    
                redisService.incr(ak+key);
            }else {
    
    
                render(response, "ACCESS_LIMIT_REACHED");
                return false;
            }
        }
        return true;
    }

    private void render(HttpServletResponse response, String cm)throws Exception {
    
    
        response.setContentType("application/json;charset=UTF-8");
        OutputStream out = response.getOutputStream();
        String str  = JSON.toJSONString(ResponseResult.error(cm));
        out.write(str.getBytes("UTF-8"));
        out.flush();
        out.close();
    }

    private User getUser(HttpServletRequest request, HttpServletResponse response) {
    
    
        String paramToken = request.getParameter(COOKI_NAME_TOKEN);
        //获取请求中的token参数
        String cookieToken = getCookieValue(request, COOKI_NAME_TOKEN);
        if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {
    
    
            return null;
        }
        String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken;
        return userService.getByToken(response, token);
    }

    private String getCookieValue(HttpServletRequest request, String cookiName) {
    
    
        Cookie[]  cookies = request.getCookies();
        if(cookies == null || cookies.length <= 0){
    
    
            return null;
        }
        for(Cookie cookie : cookies) {
    
    
            if(cookie.getName().equals(cookiName)) {
    
    
                return cookie.getValue();
            }
        }
        return null;
    }

}

其中ThreadlocalUser类:

public class ThreadlocalUser {
    
    
    private static ThreadLocal<User> userHolder = new ThreadLocal<User>();

    public static void setUser(User user) {
    
    
        userHolder.set(user);
    }

    public static User getUser() {
    
    
        return userHolder.get();
    }
}

4.controller中使用注解

 @AccessLimit(seconds=5, maxCount=5, needLogin=true)
    @RequestMapping(value="/path", method=RequestMethod.GET)
    @ResponseBody
    public Result<String> getMiaoshaPath(HttpServletRequest request, MiaoshaUser user,
    		@RequestParam("goodsId")long goodsId,
    		@RequestParam(value="verifyCode", defaultValue="0")int verifyCode
    		) {
    
    
    	if(user == null) {
    
    
    		return Result.error(CodeMsg.SESSION_ERROR);
    	}
    	boolean check = miaoshaService.checkVerifyCode(user, goodsId, verifyCode);
    	if(!check) {
    
    
    		return Result.error(CodeMsg.REQUEST_ILLEGAL);
    	}
    	String path  =miaoshaService.createMiaoshaPath(user, goodsId);
    	return Result.success(path);
    }
    

猜你喜欢

转载自blog.csdn.net/qq_41358574/article/details/121708756