zoukankan      html  css  js  c++  java
  • 模块5之实现接口限流,用户登录控制

    简介:实现自定义注解@AccessLimit,本质为一个拦截器

    定义一个注解类,一个AccessInterceptor类继成HandlerInterceptorAdapter抽象类,并重写preHandle方法。实现用户自定义某时间段内某用户对某商品的访问次数。

    第一步:创建一个注解类@Interface

     1 import java.lang.annotation.Retention;
     2 import java.lang.annotation.Target;
     3 
     4 import static java.lang.annotation.ElementType.METHOD;
     5 import static java.lang.annotation.RetentionPolicy.RUNTIME;
     6 
     7 @Retention(RUNTIME)
     8 @Target(METHOD)
     9 public @interface AccessLimit {
    10     int seconds();
    11     int maxCount();
    12     boolean needLogin() default true;
    13 }

    添加两个元注解和三个抽象方法。

    在注解类上使用的注解被称为元注解,本注解类使用了两个元注解@Retention(RUNTIME)和@Target(METHOD)。

    @Retention(RUNTIME):@Retention()注解指定@AccessLimit的生命周期,RUNTIME表示该注解在程序运行结束之前被保留。

    @Target(METHOD):@Target指定可以使用@AccessLimit的元素,METHOD参数表示可以在方法上使用该注解。更多参数请查看  java.lang.annotation.ElementType。

    第二步:创建AccessLimit.java继承抽象类HandlerInterceptorAdapter.java,重写preHandle方法。

    SpringMVC处理web请求的基本流程为,请求经过DispatcherServlet的分发后,

    按照一定的顺序执行一系列的Interceptor中的预处理方法,如果预处理方法返回true,则程序继续走向下一个预处理方法,或处理器方法(Controller中的方法);

    返回false,请求处理流程中断,此时需要通过response产生响应。

      1 import com.alibaba.druid.util.StringUtils;
      2 import com.alibaba.fastjson.JSON;
      3 import com.app.miaosha.Pojo.MiaoshaUser;
      4 import com.app.miaosha.Redis.AccessPrefix;
      5 import com.app.miaosha.Redis.RedisService;
      6 import com.app.miaosha.Result.CodeMsg;
      7 import com.app.miaosha.Result.Result;
      8 import com.app.miaosha.Service.MiaoshaUserService;
      9 import org.springframework.beans.factory.annotation.Autowired;
     10 import org.springframework.stereotype.Service;
     11 import org.springframework.web.method.HandlerMethod;
     12 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
     13 
     14 import javax.servlet.http.Cookie;
     15 import javax.servlet.http.HttpServletRequest;
     16 import javax.servlet.http.HttpServletResponse;
     17 import java.io.IOException;
     18 import java.io.OutputStream;
     19 
     20 @Service
     21 public class AccessIntercepter extends HandlerInterceptorAdapter {                                                      //1.实现HandlerInterceptorAdapter抽象类成为拦截器
     22     @Autowired
     23     MiaoshaUserService miaoshaUserService;
     24     @Autowired
     25     RedisService redisService;
     26 
     27 
     28 
     29     /*
     30     * 2.预处理方法:在请求到达处理器方法之前被调用
     31     * */
     32     @Override
     33     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
     34 
     35      
        //3.HandlerMethod:对应一个@Controller下的@RequestMapping的方法存放很多该处理器方法的信息handler:表示任意的前端传递来的请求,如对静态资源的请求
     36   if (handler instanceof HandlerMethod) {  

              MiaoshaUser user = getMiaoshaUser(request,response); 40 UserContext.setUser(user); 41 HandlerMethod hm = (HandlerMethod) handler; 42 AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);//4.得到处理器方法前是否使用了@AccessLimit 43 if (accessLimit == null) { 44 return true; 45 } 46 int seconds = accessLimit.seconds(); 47 int maxCount = accessLimit.maxCount(); 48 boolean login = accessLimit.needLogin(); 49 String key = request.getRequestURI(); 50 //需要是否需要进行登录 51 if (login) { 52 if (user == null) { 53 render(response, CodeMsg.SESSION_ERROR); 54 return false; 55 } 56 key+="_"+user.getId(); 57 }else { 58 } 59 //实现接口限流 60 Integer count = redisService.get(AccessPrefix.setExpirSeconds(seconds),""+key,Integer.class); 61 if (count==null) { //如果还没有被访问过 62 redisService.set(AccessPrefix.setExpirSeconds(seconds),""+key,1); 63 }else if (count<maxCount) { //如果访问次数没有超过规定的最大值 64 redisService.incr(AccessPrefix.setExpirSeconds(seconds),""+key); 65 }else { //如果访问超过了规定的最大值 66 render(response,CodeMsg.ACCESS_LIMIT_REACHED); 67 return false; 68 } 69 } 70 return true; 71 } 72 73 74 /* 75 * 将CodeMsg放入到response中 76 * */ 77 private void render(HttpServletResponse response, CodeMsg sessionError) throws IOException { 78 OutputStream outputStream = response.getOutputStream(); 79 response.setContentType("application/json;charset=UTF-8"); 80 String cm = JSON.toJSONString(Result.error(sessionError)); 81 outputStream.write(cm.getBytes("UTF-8")); 82 outputStream.flush(); 83 outputStream.close(); 84 } 85 /** 86 * 通过request提供的cookie获得MiaoShaUser对象 87 */ 88 private MiaoshaUser getMiaoshaUser(HttpServletRequest request, HttpServletResponse response){ 89 String paramToken = request.getParameter(MiaoshaUserService.TOKEN_NAME); 90 String cookieToken = getCookieValue(request,MiaoshaUserService.TOKEN_NAME); 91 //使用cookie得到对象 92 if (StringUtils.isEmpty(cookieToken)&&StringUtils.isEmpty(paramToken)) { 93 return null; 94 } 95 String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken; //优先取paraToken 96 MiaoshaUser user = miaoshaUserService.getByToken(token,response); 97 return user; 98 } 99 100 /* 101 * 获得request中的cookie值 102 */ 103 private String getCookieValue(HttpServletRequest request, String tokenName) { 104 Cookie[] cookies = request.getCookies(); 105 if(cookies==null || cookies.length<=0){ 106 return null; 107 } 108 for (Cookie c:cookies 109 ) { 110 if (c.getName().equals(tokenName)) { 111 return c.getValue(); 112 } 113 } 114 return null; 115 } 116 }

     上面代码实现功能的逻辑

    创建一个注解类@Interface AccessLimit,用来获取用户在注解中填写的信息。

    *

    *

    创建AccessIntercepter.java类并继承了HandlerIntercepetorAdapter.java,成为了一个拦截器。
    *
    *
    继承了HandlerIntercepetorAdapter的类成为一个拦截器,前端发送的请求会先调用一系列的拦截器的预处理方法即preHandle方法
    如果preHandle方法返回true,就继续往下执行,返回false,就使用response向前端返回异常
    *
    *
    在preHandle中开始实现接口限流功能
    *
    *
    1.判断前端发来的请求handler是否为一个匹配了@RequestMapping注解的方法可以处理的请求HandlerMethod。如果不是返回true,否则继续进行。
    *
    *
    2.判断handler对应的方法是否使用了@AccessLimit注解,如果没有返回true,否则继续进行。
    *
    *
    3.获取注解中被填写的参数值:AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);

    accessLimit中提供了方法来获取参数值。
    *
    *
    4.根据3中的参数判断是否需要登录,如果不需要登录,返回true;如果需要登录,判断登录情况,并将当前URI+user.id组合成key,等待将来使用。
    *
    *
    5.使用redis来记录用户某段时间内对某个接口的访问次数,key为4中的key,value为maxCount,expiredTime为时间长度。

    *

    *

    preHandle方法最后返回true。

  • 相关阅读:
    Linux(Centos7)下redis5安装、部署、开机自启
    请求*.html后缀无法返回json数据的问题
    Linux搭建图片服务器减轻传统服务器的压力(nginx+vsftpd)
    Centos7和Centos6防火墙开放端口配置方法(避坑教学)
    分享一个酷炫动态登录页面html
    分布式全文搜索解决方案
    PHP实现支付宝登录
    PHP发送短信
    PHP中发送qq邮件
    ES6新语法(二)
  • 原文地址:https://www.cnblogs.com/deijiawoyu/p/12671806.html
Copyright © 2011-2022 走看看