可以做h5游戲的網(wǎng)站谷歌是如何運(yùn)營的
邏輯:寫一個(gè)注解,自定義在多少秒內(nèi)限制訪問多少次。
自定義攔截器,對(duì)于加了注解的請求,在執(zhí)行方法前。先檢查有沒有注解,如果有注解就將請求的ip+url拼接作為key。
查詢r(jià)edis中有沒有該key,沒有就存入(key,1,注解中設(shè)置的時(shí)間限制,單位)
如果redis有該key,就將原來的value取出+1,
1.自定義注解
import java.lang.annotation.*;
@Inherited
@Documented
@Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {int limit() default 5;int sec() default 5;
}
2.攔截器
在springboot中自定義攔截器,實(shí)現(xiàn)HandlerInterceptor
接口。實(shí)現(xiàn)三個(gè)方法:preHandle()//請求到達(dá)controller前
postHandle()//請求到達(dá)controller后
afterCompletion()//渲染視圖后調(diào)用
import com.qcby.xmdemo.annocation.AccessLimit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Component
public class AccessLimitInterceptor implements HandlerInterceptor {@Autowiredprivate RedisTemplate redisTemplate;@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();if (!method.isAnnotationPresent(AccessLimit.class)) {return true;}AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);if (accessLimit == null) {return true;}int limit = accessLimit.limit();int sec = accessLimit.sec();String key = getIpAddr(request) + request.getRequestURI();Integer maxLimit = (Integer) redisTemplate.opsForValue().get(key);if (maxLimit == null) {redisTemplate.opsForValue().set(key, 1, sec, TimeUnit.SECONDS);//set時(shí)一定要加過期時(shí)間} else if (maxLimit < limit) {redisTemplate.opsForValue().set(key, maxLimit + 1, sec, TimeUnit.SECONDS);} else {output(response, "請求太頻繁!");return false;}}return true;}public void output(HttpServletResponse response, String msg) throws IOException {response.setContentType("application/json;charset=UTF-8");ServletOutputStream outputStream = null;try {outputStream = response.getOutputStream();outputStream.write(msg.getBytes("UTF-8"));} catch (IOException e) {e.printStackTrace();} finally {outputStream.flush();outputStream.close();}}@Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}//獲取請求ip的方法private static String getIpAddr(HttpServletRequest request) {List<String> ipHeadList = Stream.of("X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "X-Real-IP").collect(Collectors.toList());for (String ipHead : ipHeadList) {if (checkIP(request.getHeader(ipHead))) {return request.getHeader(ipHead).split(",")[0];}}return "0:0:0:0:0:0:0:1".equals(request.getRemoteAddr()) ? "127.0.0.1" : request.getRemoteAddr();}private static boolean checkIP(String ip) {return !(null == ip || 0 == ip.length() || "unknown".equalsIgnoreCase(ip));}}
3.注冊攔截器
注冊到Spring MVC的攔截器鏈中。實(shí)現(xiàn)WebMvcConfigurer
接口并重寫addInterceptors()
方法來實(shí)現(xiàn)。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class InterceptorConfig implements WebMvcConfigurer {@AutowiredAccessLimitInterceptor accessLimitInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(accessLimitInterceptor).addPathPatterns("/**")//攔截所有的路徑.excludePathPatterns("/LoginCntroller/login");}
}
4.測試
@Controller
@RequestMapping("/hello")
public class AopController {@ResponseBody@RequestMapping("/index")@AccessLimit(limit = 4,sec = 10)//加上自定義注解即可public String test (HttpServletRequest request, @RequestParam(value = "username",required = false) String userName) {return "hello!";}}