乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      美團(tuán)面試:接口被惡意狂刷,怎么辦?

       田維常 2021-08-06

      回復(fù)“電子書”獲取程序員必備電子書

      大家好,我是老田,今天給大家分享的是:接口防刷。

      之前,我已經(jīng)分享給四個美團(tuán)面試的技術(shù)點(diǎn):

      美團(tuán)面試:講清楚MySQL結(jié)構(gòu)體系,立馬發(fā)offer

      美團(tuán)面試:慢SQL有遇到過嗎?是怎么解決的?

      美團(tuán)面試:String s = new String("111")會創(chuàng)建幾個對象?

      美團(tuán)面試:為什么就能直接調(diào)用userMapper接口的方法?

      下面是原本面試現(xiàn)場:

      面試官:接口被惡意狂刷,怎么辦?

      我:這個沒搞過(每天CRUD,真的沒搞過)

      面試官:如果現(xiàn)在讓你來設(shè)計,你會怎么設(shè)計?

      我:巴拉巴拉...胡扯一通

      面試官:(帶著不耐煩的表情)我們還是換個話題吧

      .....

      為了不讓大家也和我有同樣的遭遇,今天,咱們就用一個非常簡單的方式實(shí)現(xiàn)防刷:

      一個注解搞定防刷

      技術(shù)點(diǎn)

      涉及到的技術(shù)點(diǎn)有如下幾個:

      • 自定義注解
      • 攔截器
      • Redis的基本操作
      • Spring Boot項(xiàng)目

      其實(shí),非常簡單,主要的還是看業(yè)務(wù)。

      本文主要內(nèi)容:


      自定義注解

      自定義一注解AccessLimit。

      import java.lang.annotation.Retention;
      import java.lang.annotation.Target;
       
      import static java.lang.annotation.ElementType.METHOD;
      import static java.lang.annotation.RetentionPolicy.RUNTIME;
       
      @Retention(RUNTIME)
      @Target(METHOD)
      public @interface AccessLimit { 
          //次數(shù)上限
          int maxCount();
          //是否需要登錄
          boolean needLogin()default false;
      }

      添加Redis配置項(xiàng)

      在配置文件中,加入Redis配置;

      spring.redis.database=0
      spring.redis.host=127.0.0.1
      spring.redis.port=6379
      spring.redis.jedis.pool.max-active=100
      spring.redis.jedis.pool.max-idle=100
      spring.redis.jedis.pool.min-idle=10
      spring.redis.jedis.pool.max-wait=1000ms

      注意,把Redis的starter在pom中引入。

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-data-redis</artifactId>
       </dependency>

      創(chuàng)建攔截器

      創(chuàng)建攔截器,所有請求都進(jìn)行攔截,防刷的主要內(nèi)容全部在這里。

      // 一堆import 這里就不貼出來了,需要的自己導(dǎo)入
      /**
       *  處理方法上 有 AccessLimitEnum 注解的方法
       * @author java后端技術(shù)全棧
       * @date 2021/8/6 15:42
       */

      @Component 
      public class FangshuaInterceptor extends HandlerInterceptorAdapter {

          @Resource
          private RedisTemplate<String,Object> redisTemplate;

          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

              System.out.println("----FangshuaInterceptor-----");
              //判斷請求是否屬于方法的請求
              if (handler instanceof HandlerMethod) {

                  HandlerMethod hm = (HandlerMethod) handler;

                  //檢查方法上室友有AccessLimit注解
                  AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
                  if (accessLimit == null) {
                      return true;
                  }
                  //獲取注解中的參數(shù),
                  int maxCount = accessLimit.maxCount();
                  boolean login = accessLimit.needLogin();
                  String key = request.getRequestURI();
                  //防刷=同一個請求路徑+同一個用戶+當(dāng)天
                  //如果需要登錄
                  if (login) {
                      //可以充session中獲取user相關(guān)信息
                      //這里的userId暫時寫死,
                      Long userId = 101L;
                      String currentDay = format(new Date(), "yyyyMMdd");
                      key += currentDay + userId;
                  }else{
                      //可以根據(jù)用戶使用的ip+日期進(jìn)行判斷
                  }

                  //從redis中獲取用戶訪問的次數(shù)
                  Object countCache = redisTemplate.opsForValue().get(key);
                  if (countCache == null) {
                      //第一次訪問,有效期為一天
                      //時間單位自行定義
                      redisTemplate.opsForValue().set(key,1,86400, TimeUnit.SECONDS);
                  } else{
                      Integer count = (Integer)countCache;
                      if (count < maxCount) {
                          //加1
                          count++;
                          //也可以使用increment(key)方法
                          redisTemplate.opsForValue().set(key,count);
                      } else {
                          //超出訪問次數(shù)
                          render(response, "訪問次數(shù)已達(dá)上限!");
                          return false;
                      }
                  }
              }
              return true;
          }
          //僅僅是為了演示哈
          private void render(HttpServletResponse response, String msg) throws Exception {
              response.setContentType("application/json;charset=UTF-8");
              OutputStream out = response.getOutputStream();
              out.write(msg.getBytes("UTF-8"));
              out.flush();
              out.close();
          }
          //日期格式
          public static String format(Date date, String formatString) {
              if (formatString == null) {
                  formatString = DATE_TIME_FORMAT;
              }
              DateFormat dd = new SimpleDateFormat(formatString);
              return dd.format(date);
          }
      }

      注意

      判斷是否為相同請求,使用:URI+userId+日期。即Rediskey=URI+userId+yyyyMMdd,緩存有效期為一天。

      很多都在代碼里有注釋了,另外強(qiáng)調(diào)一下,不要吐槽代碼,僅僅是演示。

      注冊攔截器

      盡管上面我們已經(jīng)自定義并實(shí)現(xiàn)好了攔截器,但還需要我們手動注冊。

      import com.example.demo.ExceptionHander.FangshuaInterceptor;
      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.WebMvcConfigurerAdapter;

      @Configuration
      public class WebConfig extends WebMvcConfigurerAdapter {
       
          @Autowired
          private FangshuaInterceptor interceptor;
       
       
          @Override
          public void addInterceptors(InterceptorRegistry registry) {
              registry.addInterceptor(interceptor);
          }
      }

      這樣我們的注解就正式注冊到攔截器鏈中了,后面項(xiàng)目中才會有效。

      使用注解

      前面的準(zhǔn)備都搞定了,現(xiàn)在來具體使用。

      首先,我們創(chuàng)建一個簡單的controller,然后,在方法上加上我們自定義的注解AccessLimit,就可以實(shí)現(xiàn)接口防刷了。

      import com.example.demo.result.Result;
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.ResponseBody;
       
      @Controller
      public class FangshuaController {
          //具體請求次數(shù)由具體業(yè)務(wù)決定,以及是否需要登錄
          @AccessLimit(maxCount=5, needLogin=true)
          @RequestMapping("/fangshua")
          @ResponseBody
          public Object fangshua(){
              return "請求成功";
       
          }
      }

      測試,瀏覽器頁面上訪問:http://localhost:8080/fangshua

      前面4次返回的是:請求成功

      超過4次后變成:訪問次數(shù)已達(dá)上限!

      一個注解就搞定了,是不是 so easy !??!

      總結(jié)

      關(guān)于接口防刷,如果在面試中被問到,至少還是能說個123了。也建議大家手動試試,自己搞出來了更帶勁兒。

      好了,今天就分享到此,如果對你有幫助,記得點(diǎn)贊、分享在看。

      參考:blog.csdn.net/weixin_42533856

        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多