定义拦截器,接口限流防刷

一、自定义注解

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); 
}

}

扫描二维码关注公众号,回复: 1388949 查看本文章

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);
}

猜你喜欢

转载自blog.csdn.net/jiuweideqixu/article/details/80502486