其他分享
首页 > 其他分享> > API接口防刷

API接口防刷

作者:互联网

API接口防刷


1.缓解服务器压力
接口频繁,可能 1 秒上千次,限制非正常访问可以缓解服务器压力。
2.节约运营成本
短信接口被请求一次,会触发几分钱的运营商费用。
······

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 {

    int seconds();
    int maxCount();

}

限制访问拦截器

**
 * 防刷拦截器
 */
@Component
public class AccessLimitInterceptor implements HandlerInterceptor {

    @Autowired
    private RedisService redisService;

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

        //方法请求拦截
        if(handler instanceof HandlerMethod){

            HandlerMethod hm = (HandlerMethod) handler;

            //获取方法中的AccessLimit注解
            AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
            if(accessLimit == null){
                return true;
            }
            int seconds = accessLimit.seconds();
            int maxCount = accessLimit.maxCount();
            String uri = request.getRequestURI();
            //这里简单用用户名代替,建议用通过校验的token
            String token = "username";
            String key = token+uri;
            //从redis中获取用户访问的次数
            Integer count = redisService.getString(key,Integer.class);
            if(count == null){
                //第一次访问
                redisService.setString(key,"1", seconds, TimeUnit.SECONDS);
            }else if(count < maxCount){
                //加1
                redisService.incr(key);
            }else{
                //超出访问次数
                render(response,CodeMsg.ACCESS_LIMIT_REACHED);
                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.codeMsg(cm));
        out.write(str.getBytes("UTF-8"));
        out.flush();
        out.close();
    }

}

拦截器注册

import com.example.book.interceptor.AccessLimitInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {

    @Bean
    public AccessLimitInterceptor getFangshuaInterceptor(){
        return new AccessLimitInterceptor();
    }

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
//        registry.addInterceptor(getFangshuaInterceptor()).addPathPatterns("/**");
        registry.addInterceptor(getFangshuaInterceptor());
        super.addInterceptors(registry);
    }

}

Controller添加AccessLimit 注解

import com.example.book.annotation.AccessLimit;
import com.example.book.result.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
public class BookController {

    @AccessLimit(seconds=5, maxCount=5)
    @GetMapping("/book")
    public Result book(){
        Map map = new HashMap<>();
        map.put("bookName","hello world");
        map.put("author","god");
        return Result.success(map);
    }

}

redis简单操作

RedisService 接口

import java.util.concurrent.TimeUnit;

public interface RedisService {

    void setString(String key, String value);

    void setString(String key, String value, long timeOut);

    void setString(String key, String value, long timeOut, TimeUnit timeUnit);

    String getString(String key);

    <T> T getString(String key, Class<T> clazz);

    void incr(String key);
}

RedisService 实现类

import com.alibaba.fastjson.JSON;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Component
public class RedisServiceImpl implements RedisService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 设置key
     * @param key key
     * @param value value
     */
    public void setString(String key, String value) {
        ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
        valueOperations.set(key, value);
    }

    /**
     * 设置key
     * @param key
     * @param value
     * @param timeOut
     */
    public void setString(String key, String value, long timeOut) {
        ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
        valueOperations.set(key, value, timeOut);
    }

    /**
     * 设置key
     * @param key
     * @param value
     * @param timeOut
     * @param timeUnit
     */
    public void setString(String key, String value, long timeOut, TimeUnit timeUnit) {
        ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
        valueOperations.set(key, value, timeOut, timeUnit);
    }

    /**
     * 获取key
     * @param key key
     * @return
     */
    public String getString(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    /**
     * 返回指定类型数据
     * @param key
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T getString(String key, Class<T> clazz) {
        String jsonValue = stringRedisTemplate.opsForValue().get(key);
        if(null == jsonValue){
            return null;
        }
        return JSON.parseObject(jsonValue, clazz);
    }

    /**
     * 加1
     * @param key
     */
    @Override
    public void incr(String key) {
        stringRedisTemplate.opsForValue().increment(key);
    }

}

简化版统一返回

返回enum

public enum CodeMsg {

    SUCCESS(200, "成功"),
    ERROR(500, "服务器异常"),
    ACCESS_LIMIT_REACHED(403,"已达到访问限制");

    private int code;

    private String msg;

    CodeMsg(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

}

返回实体

public class Result<T> {

    private int code;

    private String msg;

    private T data;

    private Result() {
    }

    protected Result(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    protected Result(CodeMsg cm) {
        this.code = cm.getCode();
        this.msg = cm.getMsg();
    }

    protected Result(CodeMsg cm, T data) {
        this.code = cm.getCode();
        this.msg = cm.getMsg();
        this.data = data;
    }

    public static <T> Result success(T data) {
        return new Result(CodeMsg.SUCCESS, data);
    }

    public static Result codeMsg(CodeMsg cm) {
        return new Result(cm);
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

标签:code,防刷,String,接口,API,key,msg,import,public
来源: https://blog.csdn.net/l_learning/article/details/115766234