1. 程式人生 > >定義攔截器,介面限流防刷

定義攔截器,介面限流防刷

一、自定義註解

package com.mydre.miaosha.access;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccessLimit {
int seconds();
int maxCount();
boolean needLogin() default true;
//然後需要定義攔截器

}

二、定義攔截器

package com.mydre.miaosha.access;
import java.io.OutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import com.alibaba.fastjson.JSON;
import com.mydre.miaosha.domain.MiaoshaUser;
import com.mydre.miaosha.redis.AccessKey;
import com.mydre.miaosha.redis.RedisService;
import com.mydre.miaosha.result.CodeMsg;
import com.mydre.miaosha.service.MiaoshaUserService;
@Service


public class AccessInterceptor extends HandlerInterceptorAdapter{
@Autowired
MiaoshaUserService miaoshaUserService;
@Autowired
RedisService redisService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if(handler instanceof HandlerMethod){
MiaoshaUser user = getMiaoshaUser(request, response);
//將使用者儲存在ThreadLoal中  新建類UserContext
UserContext.setUser(user); 
HandlerMethod hm = (HandlerMethod) handler;
AccessLimit al = hm.getMethodAnnotation(AccessLimit.class);

if(al == null){
return true;//true表示驗證通過,註解為空,說明不需要攔截(也就是根本不需要驗證,等於驗證通過)
}
//往下走,說明攔截器需要做一些事情
int maxCount = al.maxCount();
int seconds = al.seconds();
boolean needLogin = al.needLogin();
String key = request.getRequestURI();
if(needLogin == true){
if(user == null){
//不進入頁面,但是仍然需要給出提示
render(response, CodeMsg.SESSION_ERROR);
return false;
}
key += "_" + user.getId();//需要登入,且取出了使用者
}else{
//doNothing
}
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 codeMsg) throws Exception{//異常是可以丟擲的
response.setContentType("application/json;charset=UTF-8");//防止亂碼
OutputStream  ops = response.getOutputStream();
String str = JSON.toJSONString(codeMsg);
ops.write(str.getBytes("UTF-8"));//往記憶體中寫的是位元組
ops.flush();
ops.close();
}
private MiaoshaUser getMiaoshaUser(HttpServletRequest request, HttpServletResponse response){
String paramToken = request.getParameter(MiaoshaUserService.COOKIE1_NAME_TOKEN);
String cookieToken = getCookieValue(request, MiaoshaUserService.COOKIE1_NAME_TOKEN);
if(StringUtils.isEmpty(paramToken) && StringUtils.isEmpty(cookieToken)){
return null;
}
String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken;
MiaoshaUser user = miaoshaUserService.getByToken(response, token);//給你一個響應你才能在下一個頁面檢視
return user;
}
private String getCookieValue(HttpServletRequest request, String cookieName) {
Cookie[] cookies = request.getCookies();
if(cookies == null || cookies.length <= 0){
return null;
}
for(Cookie cookie : cookies){
if(cookie.getName().equals(cookieName)){
return cookie.getValue();
}
}
return null;
}
}

註冊攔截器

package com.mydre.miaosha.config;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import com.mydre.miaosha.access.AccessInterceptor;
@Configuration   //這裡一定要加上這個註解,可以載入配置資訊
public class WebConfig extends WebMvcConfigurerAdapter{
@Autowired
UserArgumentResolver userArgumentResolver;
@Autowired
AccessInterceptor accessInterceptor;
/*
* 把resolver註冊進來
* */
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(userArgumentResolver);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.
addInterceptor(accessInterceptor);
}

}

重構UserArgumentResolver

package com.mydre.miaosha.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import com.mydre.miaosha.access.UserContext;
import com.mydre.miaosha.domain.MiaoshaUser;
import com.mydre.miaosha.service.MiaoshaUserService;
@Service
public class UserArgumentResolver implements HandlerMethodArgumentResolver{
@Autowired
MiaoshaUserService miaoshaUserService;
public boolean supportsParameter(MethodParameter parameter) {
Class<?> clazz = parameter.getParameterType(); 
return clazz == MiaoshaUser.class;
}
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
//這個函式在攔截器之後執行,直接獲取 攔截器中已經存入到ThreadLocal中的user即可
return UserContext.getUser();//攔截器執行時 執行了 UserContext.setUser(user); 
}

}

ThreadLocal

package com.mydre.miaosha.access;
import com.mydre.miaosha.domain.MiaoshaUser;
//ThreadLocal ,多執行緒時執行緒安全的一種訪問方式,跟當前的執行緒繫結,如果是多執行緒,每個執行緒裡面單獨存一份,攔截器獲取了使用者
public class UserContext {
private static ThreadLocal<MiaoshaUser> threadLocal = new ThreadLocal<MiaoshaUser>();
public static void setUser(MiaoshaUser user){
threadLocal.set(user);
}
public static MiaoshaUser getUser(){
return threadLocal.get();
}

}

MiaoshaController

        @AccessLimit(seconds=10, maxCount=5, needLogin=true) //限制10秒鐘訪問這個介面5次
@RequestMapping(value="/generate_path", method=RequestMethod.GET)
@ResponseBody
    public Result<String> generate_path(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 pass = miaoshaService.passVerifyCode(user,goodsId,verifyCode);
if(!pass){
return Result.error(CodeMsg.VERIFY_CODE_ERROR);
}
String genPath = miaoshaService.createPath(user, goodsId);//產生並存入到redis資料庫中
return Result.success(genPath);
}