美团面试:接口被恶意狂刷,怎么办?

下面是原本面试现场:

面试官:接口被恶意狂刷,怎么办?

我:这个没搞过(每天CRUD,真的没搞过)

面试官:如果现在让你来设计,你会怎么设计?

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

面试官:(带着不耐烦的表情)我们还是换个话题吧

.....

为了不让大家也和我有同样的遭遇,今天,咱们就用一个非常简单的方式实现防刷:

一个注解搞定防刷

技术点

涉及到的技术点有如下几个:

  • 自定义注解
  • 拦截器
  • Redis的基本操作
  • Spring Boot项目

其实,非常简单,主要的还是看业务。

机-会

技术大厂,前端-后端-测试,全国均有机会,感兴趣可以试试。待遇和稳定性都还不错~

自定义注解

自定义一注解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 maxCount();
    //是否需要登录
    boolean needLogin()default false;
}

添加Redis配置项

在配置文件中,加入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中引入。

xml 体验AI代码助手 代码解读复制代码<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>

创建拦截器

创建拦截器,所有请求都进行拦截,防刷的主要内容全部在这里。

// 一堆import 这里就不贴出来了,需要的自己导入
/**
 *  处理方法上 有 AccessLimitEnum 注解的方法
 * @author java后端技术全栈
 * @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;
            }
            //获取注解中的参数,
            int maxCount = accessLimit.maxCount();
            boolean login = accessLimit.needLogin();
            String key = request.getRequestURI();
            //防刷=同一个请求路径+同一个用户+当天
            //如果需要登录
            if (login) {
                //可以充session中获取user相关信息
                //这里的userId暂时写死,
                Long userId = 101L;
                String currentDay = format(new Date(), "yyyyMMdd");
                key += currentDay + userId;
            }else{
                //可以根据用户使用的ip+日期进行判断
            }

            //从redis中获取用户访问的次数
            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 {
                    //超出访问次数
                    render(response, "访问次数已达上限!");
                    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,缓存有效期为一天。

很多都在代码里有注释了,另外强调一下,不要吐槽代码,仅仅是演示。

注册拦截器

尽管上面我们已经自定义并实现好了拦截器,但还需要我们手动注册。

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);
    }
}

这样我们的注解就正式注册到拦截器链中了,后面项目中才会有效。

使用注解

前面的准备都搞定了,现在来具体使用。

首先,我们创建一个简单的controller,然后,在方法上加上我们自定义的注解AccessLimit,就可以实现接口防刷了。

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 {
    //具体请求次数由具体业务决定,以及是否需要登录
    @AccessLimit(maxCount=5, needLogin=true)
    @RequestMapping("/fangshua")
    @ResponseBody
    public Object fangshua(){
        return "请求成功";
 
    }
}

测试,浏览器页面上访问:http://localhost:8080/fangshua

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

超过4次后变成:访问次数已达上限!

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

总结

关于接口防刷,如果在面试中被问到,至少还是能说个123了。也建议大家手动试试,自己搞出来了更带劲儿。

——转载自:Java编程爱好者

#面试时最害怕被问到的问题#
全部评论
遇到刷接口的 就私聊他 告诉他求求你不要刷了
7 回复 分享
发布于 01-12 18:19 陕西
aop也行
1 回复 分享
发布于 01-12 21:25 浙江

相关推荐

双非本科,211硕士。自学java半年,想去找一个实习,求大佬们锐评一下简历
nsjbambmbs:简历一写就是微服务,一问实际就俩服务,简历一写就是高并发一问 QPS 个位数既然写了微服务,那我出系统设计题场景题也没啥问题吧
点赞 评论 收藏
分享
头像
01-12 14:44
已编辑
百度_高级研发工程师
今天看到了某平台攻击牛友的帖子,段段今天打算为牛友们说句话,我们的努力到底有没有意义。&nbsp;(原文复述:感觉牛客就是当年那群做题区毕业了开始找工作还收不住那股味,颇有一种从年级第一掉到年纪第二后抱怨考不上大学的区味)&nbsp;&nbsp;粗鄙,无礼,傲慢,攻击,在这里我没有看到任何有用的分析,我只看到了屁股决定脑袋的攻击,我只看到了嫉妒和眼红。一、去医院不看病你去逛街吗&nbsp;去医院你不去看病你去逛街吗?去加油站不加油你去抽烟吗?去部队你不训练战斗技能你去养老吗?来牛客你不努力求职你来干什么来了。&nbsp;牛客本身就是个求职平台,大家分享有用的知识,分享面经,分享offer,分享求职经验的,来牛客不就干这个来了吗?有什么问题吗?...
给个好点的工作吧啊啊...:不知道我看的是不是和博主同样的帖子,我记得原帖是表达的是有些匿名老是发几十万的offer侮辱价,然后就有牛友觉得凡尔赛了导致后面的评论有些偏激。我觉得这个最近闫学晶那个事情有点类似了,她说他儿子一年只能赚七八十万家庭生活都难以为继,不说普通家庭,多少大厂的程序员都赚不到这个数字,大部分家庭看到这种发言肯定会难受的一p,生活的担子又这么重,人都是需要发泄情绪的,互联网就是个极佳的载体,所以很多人直接就喷她了,人在情绪发泄的时候是不思考的,否则就不叫发泄了。然后还有一个点,段哥假定了这些喷的人全都是“躺平的”,这点可能有失偏颇,很多人一直在努力,但是始终缺乏天时地利人和的某一个条件,这点相信段哥找工作的过程中深有体会。绝大部分人都以结果的失败去否认了努力的全过程,可能只是别人努力的方向错了。就像一次面试,可能你准备了很久,结果面试官就是比较奇葩,一直问没有学习到的领域或知识点,然后有人凭一个挂掉的结果就直接给你扣了一个“躺平”的帽子,觉得挂掉是你不够努力,您心里滋味如何?再说点近点的,我也是od,多少同事深夜无偿加班,涨过一分工资吗?多少外包的技术大牛因为学历被困在外包,连od都进不去,这些人难道不努力吗?只是限制与生活、公司制度等等之类的无奈罢了。说到努力,又想到李家琦79元眉笔事件,这么多年有没有认真工作?有没有涨工资?他嘴里说出来是那么的理所当然,打工牛马都知道“任劳任怨”,“认真工作”真能涨工资?只干活不发声就等着被摘果子吧,企业里永远都是“汇报杰出者”升的最快(当然不是所有企业),这种事情相信段哥包括我甚至大部分od都经历过。最近辞职回老家,和老爸散步每次他都会感慨街上的蔬菜小贩多不容易,他们晚上就窝在那种三轮小货车的驾驶室里,腿都伸不直,我们这里晚上零下了,只盖一条薄毛毯,始终舍不得住我们镇上几十块的酒店,因为一车蔬菜就赚几百块顶多一千而且要卖好久,这样的例子还有太多了。这种芸芸众生可能辛苦了一天之后,打开手机看到网上的凡尔赛发言,跟风喷了几句发泄情绪,我觉得这种人不应该扣上“躺平”的帽子。我觉得大部分正常人都是努力的,或者曾经努力过,但世界上有太多努力解决不了的无奈了,甚至说你都没有那个努力的机会,不过正因如此,才显得坚持不懈的努力奋斗之人的难得可贵,认清生活的真相后仍然热爱生活,敢于直面现实的淋漓。
段段STEADY觉醒与突...
点赞 评论 收藏
分享
joecii:如果没有工资,那可能没有工资是这家公司最小的问题了
找实习记录
点赞 评论 收藏
分享
评论
4
14
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务