1
3

More than 3 years have passed since last update.

SpringBoot + RedisでAPIへアクセス回数の制限機能を実装してみよう

Last updated at Posted at 2019-12-25

はじめに

いまのところはAPIインターフェースを使う場合はめっちゃ多いと思います。ところが、APIを呼び出すの回数は多すぎたら、サーバー側の処理は遅延になるかもしれません。何か悪い影響が起こるということです。
 今回はAPIへアクセス回数を制限するために、よく調べたり、深く考えたりしたうえでこの記事を書いていました。

環境

SpringBootの知識もRedisの知識も持っている必要です。

  • Java
  • SpringBoot
  • Redis

方法

実装方針としては、主にSpringBootとRedisを用いてアノテイションの形で実装する。
細かいところはコメントアウトを見れば、分かると思います。

1. アノテイションクラスを作成

AccessLimit.java
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * @author Hyman
 * @date 2019/12/25 11:12
 */
@Retention(RUNTIME)
@Target(METHOD)
public @interface AccessLimit {

    // 制限時間(秒単位)
    int seconds();
    // 最大アクセス回数
    int maxCount();
    // ログイン状況
    boolean needLogin() default true;
}

2. インターセプタークラスを作成

ApiLimitInterceptor.java
import com.alibaba.fastjson.JSON;
import com.example.demo.action.AccessLimit;
import com.example.demo.redis.RedisService;
import com.example.demo.result.CodeMsg;
import com.example.demo.result.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;

/**
 * @author Hyman
 * @date 2019/12/25 11:22
 */
@Component
public class ApiLimitInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private RedisService redisService;

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

        //リクエストはHandlerMethodかどうかを判明する
        if(handler instanceof HandlerMethod){

            HandlerMethod hm = (HandlerMethod) handler;

            // メソッドの上のアノテイションを取得し、AccessLimitアノテーションがあるかどうかが分かるよう
            AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
            if(accessLimit == null){
         // AccessLimitアノテーションがなければ、何もしない
                return true;
            }
       
            //  制限時間を取得する
            int seconds = accessLimit.seconds();
            //  最大アクセス回数を取得する
            int maxCount = accessLimit.maxCount();
            boolean login = accessLimit.needLogin();
            String key = request.getRequestURI();

            // ログインが必須であれば、以下の処理を行う
            if(login){
                // ここでは認証の部分を省く
                // key+=""+"userInfo";
            }

            // redisからアクセス回数を取得する
            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){
                // アクセス回数+1
                redisService.incr(ak,key);
            }else{
                //最大アクセス回数を超えたら、エラーメッセージを作って戻ります。
                render(response,"指定の時間以内、最大アクセス回数を超えます。"); //
                return false;
            }
        }

        return true;

    }

 /**
  * 最大アクセス回数を超えたら、エラーメッセージを作って戻ります。
  */
  private void render(HttpServletResponse response, CodeMsg cm)throws Exception {
        response.setContentType("application/json;charset=UTF-8");
        OutputStream out = response.getOutputStream();
        String str  = JSON.toJSONString(Result.error(cm));
        out.write(str.getBytes("UTF-8"));
        out.flush();
        out.close();
    }
}

3. インターセプタークラスを配置

上記2で作ったインターセプタークラスをSpringBootに登録する。

WebConfig.java
import com.example.demo.ExceptionHander.ApiLimitInterceptor;
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;

/**
 * @author Hyman
 * @date 2019/11/31 15:58
 */
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Autowired
    private ApiLimitInterceptor interceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(interceptor);
    }
}

4. コントローラーを作成

コントローラーを作って、メソッドの上に@AccessLimitを付いてます。
それが終わったら、Postmanでテストを行いましょう。

ApiLimitController.java
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;

/**
 * @author Hyman
 * @date 2019/12/25 13:21
 */
@Controller
public class ApiLimitController {

    // 時間:5秒 ,最大アクセス回数:5 ,ログイン状況:必須
    @AccessLimit(seconds=5, maxCount=5, needLogin=true)
    @RequestMapping("/ApiLimitTest")
    @ResponseBody
    public Result<String> ApiLimitTest(){
        return Result.success("リクエスト成功!");
    }
 }

最後に

最後まで読んでいただき、ありがとうございます。
おかしいと思う部分は遠慮なくご指摘いただければと思います。
よろしくお願いします。

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3