采用注解的方式
1)定义一个注解
@Retention(RUNTIME) @Target(METHOD) public @interface AccessLimit { int seconds(); int maxCount(); }
2)在需要限流的方法前加这样的注解
@AccessLimit(seconds=5, maxCount=5)
3)在拦截器里进行判断,看方法是否使用了AccessLimit注解修饰
@Component public class AccessInterceptor extends HandlerInterceptorAdapter{ @Autowired MiaoshaUserService miaoshaUserService; @Autowired RedisService redisService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("prehandle...."); if(handler instanceof HandlerMethod) { HandlerMethod hm = (HandlerMethod)handler; String methodName = hm.getMethod().getName(); if(methodName.equals("toLogin") || methodName.equals("do_login")){ return true; }else{ //获取用户登录信息放入线程上下文中 MiaoshaUser user = getUser(request, response); UserContext.setUser(user); //判断防刷限流注解 AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class); if(accessLimit == null) { return true; } int seconds = accessLimit.seconds(); int maxCount = accessLimit.maxCount(); String key = request.getRequestURI(); AccessKey ak = AccessKey.withExpire(seconds); Integer count = redisService.get(ak, key, Integer.class); if(count == null) { redisService.set(ak, key, 1); }else if(count < maxCount) { redisService.incr(ak, key); }else { render(response, CodeMsg.ACCESS_LIMIT_REACHED); return false; } } } return true; } private void render(HttpServletResponse response, CodeMsg cm)throws Exception { response.setContentType("application/json;charset=UTF-8"); OutputStream out = response.getOutputStream(); String str = JSON.toJSONString(Result.error(cm)); out.write(str.getBytes("UTF-8")); out.flush(); out.close(); } private MiaoshaUser getUser(HttpServletRequest request, HttpServletResponse response) { String paramToken = request.getParameter(MiaoshaUserService.COOKI_NAME_TOKEN); String cookieToken = getCookieValue(request, MiaoshaUserService.COOKI_NAME_TOKEN); if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) { throw new GlobalException(CodeMsg.SESSION_ERROR); } String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken; //这里是从redis中取用户信息 MiaoshaUser miaoshaUser = miaoshaUserService.getByToken(response, token); if(null == miaoshaUser){ throw new GlobalException(CodeMsg.SESSION_ERROR); } return miaoshaUser; } 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; } }
public class UserContext { private static ThreadLocal<MiaoshaUser> userHolder = new ThreadLocal<MiaoshaUser>(); public static void setUser(MiaoshaUser user) { userHolder.set(user); } public static MiaoshaUser getUser() { return userHolder.get(); } }
思路是:用户第一次请求过来后,将url和用户id拼接作为key,1作为value值设置到redis中,并设置过期时间。用户下次请求过来,采用redis的自增,当value值超过最大访问次数时,拒绝用户访问。
原文地址:https://www.cnblogs.com/moris5013/p/12350059.html
时间: 2024-11-09 07:06:05