apiインタフェース限流防止悪意のあるブラシインタフェース


apiストリーム制限シーン
ストリーム制限の要件は、多くの一般的なシーンで発生します.
1.秒殺活動、誰かがソフトウェアを使って悪意を持って注文して商品を奪って、流れを制限して機械が活動に参加することを防止する必要がある.あるapiは様々なシステムに広く呼び出され、ネットワーク、メモリなどの資源を深刻に消費し、合理的にストリームを制限する必要がある.宝を洗ってipの所在する都市のインタフェースを獲得して、微信の公衆番号は微信のユーザーを識別するなどの開発インタフェースを開発して、無料でユーザーに提供する時流を制限する必要があって、更にリアルタイム性と正確性のインタフェースは料金を払う必要があります.
api限流実戦
まず、注釈クラスAccessLimitを作成し、注釈方式を使用して方法の上限ストリームをより優雅で便利にします.3つのパラメータはそれぞれ有効時間、最大アクセス回数、ログインが必要かどうかを表し、seconds内でmaxCountに最大アクセスする回数と理解できる.
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;
}


限流の考え方
1.パス:ipのキーとして、アクセス回数がvalueであることにより、あるユーザの要求を一意に識別する.アクセス毎にkeyが存在するか否かを判断し、countが制限アクセス回数を超えているか否かを判断する.アクセスが制限を超えた場合、responseはmsgに戻る:spring AOPを使用して注釈ブロックを表示するためにフロントエンドに頻繁に要求する
import com.learn.springbootredis.annotation.AccessLimit;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class AccessLimitAop {

    @Autowired
    private RedisTemplate redisTemplate;

    @Pointcut(value = "@annotation(com.learn.springbootredis.annotation.AccessLimit)")
    public void cutLimit(){}

    @Around("cutLimit()")
    public Object recordSysLog(ProceedingJoinPoint point)throws Throwable{
        MethodSignature ms = (MethodSignature) point.getSignature();
        Method method = ms.getMethod();
        AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        if (null == accessLimit) {
            return point.proceed();
        }
        int seconds = accessLimit.seconds();
        int maxCount = accessLimit.maxCount();
        boolean needLogin = accessLimit.needLogin();

        if (needLogin) {
            //      
        }
        String key = ms.getName() + ":" + getIpAddress( request) ;

        Integer count = redisTemplate.opsForValue().get(key);
        Long expire = redisTemplate.getExpire(key);
        if (null == count || -1 == count) {
            redisTemplate.opsForValue().set(key, 1,seconds, TimeUnit.SECONDS);
            return point.proceed();
        }

        if (count < maxCount) {
            redisTemplate.opsForValue().increment(key);
            return point.proceed();
        }

        if (count >= maxCount) {
// response    json            
            String str = "{
" + "\t\"success\": 1,
" + "\t\"message\": \" \"
" + "}"; return str; } return point.proceed(); } /** * IP , request.getRemoteAddr(); IP 。 * , ,X-Forwarded-For , IP , IP ? * X-Forwarded-For unknown IP * @param request * @return */ private static String getIpAddress(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); if("127.0.0.1".equals(ip)||"0:0:0:0:0:0:0:1".equals(ip)){ // IP InetAddress inet=null; try { inet = InetAddress.getLocalHost(); } catch (UnknownHostException e) { e.printStackTrace(); } ip= inet.getHostAddress(); } } return ip; } }

注記@AccessLimitをcontroller層メソッド上で直接使用
import com.learn.springbootredis.annotation.AccessLimit;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
public class TestController {

    @RequestMapping("questTest")
    @AccessLimit(seconds = 10,maxCount = 1)
    public Object questTest(){
        Map map = new HashMap();
        map.put("success",0);
        map.put("message","  ");
        return map;
    }
}