美团面试:接口被恶意狂刷,怎么办? 心已赠人 2022-09-03 11:12 119阅读 0赞 关注公众号“Java后端技术全栈” 回复“电子书”获取程序员必备电子书 大家好,我是老田,今天给大家分享的是:**接口防刷**。 之前,我已经分享给四个美团面试的技术点: [**美团面试:讲清楚MySQL结构体系,立马发offer**][MySQL_offer] [美团面试:慢SQL有遇到过吗?是怎么解决的?][SQL] [美团面试:String s = new String("111")会创建几个对象?][String s _ new String_111] [美团面试:为什么就能直接调用userMapper接口的方法?][userMapper] 下面是原本面试现场: > 面试官:接口被恶意狂刷,怎么办? > > 我:这个没搞过(每天CRUD,真的没搞过) > > 面试官:如果现在让你来设计,你会怎么设计? > > 我:巴拉巴拉...胡扯一通 > > 面试官:(带着不耐烦的表情)我们还是换个话题吧 > > ..... 为了不让大家也和我有同样的遭遇,今天,咱们就用一个非常简单的方式实现防刷: > 一个注解搞定防刷 ## 技术点 ## 涉及到的技术点有如下几个: * 自定义注解 * 拦截器 * `Redis`的基本操作 * `Spring Boot`项目 其实,非常简单,主要的还是看业务。 本文主要内容: ![c341b8324a7afae8fb645af3ed10a4a1.png][] ## 自定义注解 ## 自定义一注解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`中引入。 <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+日期`。即`Redis`的`key`=`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了。也建议大家手动试试,自己搞出来了更带劲儿。 好了,今天就分享到此,如果对你有帮助,记得`点赞`、`分享`、`在看`。 参考:blog.csdn.net/weixin\_42533856 推荐阅读 * [你不动,我就不动---观察者模式][---] * [离职后,10天面试 4 家公司的总结][10_4] * [一套Spring Cloud Alibaba视频教程][Spring Cloud Alibaba] * [后端开发人员,学习之路][Link 1] * [后端面试 23万,500多页,牛逼!][23_500] * [112期面试汇总,建议收藏][112] [MySQL_offer]: http://mp.weixin.qq.com/s?__biz=MzU4MDM3MDgyMA%3D%3D&chksm=fd557ab0ca22f3a6754bdd71d976fd22bb3bc65e87f059c9a74f1db3e01f8fdff8d9844ac3e8&idx=1&mid=2247504475&scene=21&sn=11130bc937218b5e052c10cd8fa85a0c#wechat_redirect [SQL]: http://mp.weixin.qq.com/s?__biz=MzU4MDM3MDgyMA%3D%3D&chksm=fd557949ca22f05fdf97feeb9f339951bfb62a592823578ae02b557d8f79022e6c02ab85aaab&idx=1&mid=2247504290&scene=21&sn=8431458c6d46bdccc8a91afd95f906de#wechat_redirect [String s _ new String_111]: http://mp.weixin.qq.com/s?__biz=MzU4MDM3MDgyMA%3D%3D&chksm=fd554e7cca22c76a2c897360afa9e0c6f4ed7da42a571673158c2524cf46b2fea901163a474b&idx=1&mid=2247493271&scene=21&sn=f3c5bf9f67971ee6c9dacc0eb757b0e3#wechat_redirect [userMapper]: http://mp.weixin.qq.com/s?__biz=MzU4MDM3MDgyMA%3D%3D&chksm=fd555a1dca22d30b64ae0760054523998b79277c5b6886813011504b7e717a8c29159223596f&idx=1&mid=2247496438&scene=21&sn=02020dac3415ac5d1887b88053d30ffb#wechat_redirect [c341b8324a7afae8fb645af3ed10a4a1.png]: /images/20220829/8cdca16ed1534f698ba88c8ff6a93cf1.png [---]: http://mp.weixin.qq.com/s?__biz=MzU4MDM3MDgyMA%3D%3D&chksm=fd5564b1ca22eda7a8dc765c57b3c09d4c16f1a43d34fe0202ff8fa9b3ee55a4b529ce18c506&idx=1&mid=2247502938&scene=21&sn=1042c8c297a7c92f5437bfbaaf2072eb#wechat_redirect [10_4]: http://mp.weixin.qq.com/s?__biz=MzU4MDM3MDgyMA%3D%3D&chksm=fd556459ca22ed4faf44bc01dbab47030bae7995cb0bcaa91b55a9e130b2a9e0be119dea29ef&idx=1&mid=2247503026&scene=21&sn=183d38a1172e3eb4d31f60783c477c38#wechat_redirect [Spring Cloud Alibaba]: http://mp.weixin.qq.com/s?__biz=MzU4MDM3MDgyMA%3D%3D&chksm=fd556225ca22eb33a3859702053e4f6817f646264cf58fabe5c9f18144da347305a2a6d8b0e6&idx=2&mid=2247502542&scene=21&sn=efbdc5883e72041b423df9dd229723f1#wechat_redirect [Link 1]: http://mp.weixin.qq.com/s?__biz=MzU4MDM3MDgyMA%3D%3D&chksm=fd555753ca22de4522b62563737854b0359847f046b416df20cdfa099ea81d8e03c8bb22f4d1&idx=1&mid=2247499704&scene=21&sn=e3a867f360e8a8cccf8d79e0652c36c0#wechat_redirect [23_500]: http://mp.weixin.qq.com/s?__biz=MzU4MDM3MDgyMA%3D%3D&chksm=fd556535ca22ec2350b399f33fd1125ce75dd34f1351b47cb70c2a60ffad803853209fd083b4&idx=1&mid=2247503326&scene=21&sn=a35bd6b6dd62d742f34c7c80e8f8f421#wechat_redirect [112]: http://mp.weixin.qq.com/s?__biz=MzU4MDM3MDgyMA%3D%3D&chksm=fd556527ca22ec310441fb12453e0bd624fb0e4bfa18919d1141d3641b9c462a9a28113dc95a&idx=2&mid=2247503308&scene=21&sn=0464ed003b29d85ae47f5b330cade32e#wechat_redirect
还没有评论,来说两句吧...