今日面试某信息的场景题提到了下这个问题,没太搞懂面试官具体想让我说的就是两个接口的设计,一个生成验证码,一个校验验证码,和其具体的设计方案,没听懂当时的意思,有的小懵逼,因此回头总结回顾一下,日常开发中对这些小细节还是要注重一下,不能因为场景逻辑简单就不去分析其背后的原因。
设计登陆验证码的时候有个重要的点就是接口防刷与唯一id校验,防止别人暴力的时候使用到了数据库正常的验证码,导致其他人无法登陆,虽然这个概率有些小吧。
先上普通登陆业务图
首先要考虑的点:
1.验证码防恶意刷
2.验证码输入错误后应对数据库查询账户密码匹配做一个拦截
3.验证码存储在哪里,成功后记得删除该条验证码
4.唯一ID的生成逻辑
5.前端接口暴露什么,生成验证码的url暴露了怎么防恶意刷
根据上述分析,我们需要分别实现验证码的生成、展示、id生成、存储的接口,一般验证码服务需要对外提供生成验证码和校验验证码的api,我们写在一个接口中,四个组件以内部接口类的形式进行开发。验证码生成公式与结果可以抽出来为工具类,公示转图片也为工具类。
防止url恶意刷可以通过url加密(拦截请求url,对上一级url如login拦截,获取login/后的密文,解析后与login中方法进行比较,存在则转发正确的路由),还有就是合理的限流方案,比如redis滑动窗口(见前文),nginx配置限流策略(limit_req_zone 和 limit_req 两个指令,限制单个IP的请求处理速率)(Nginx 限流方式(速率限流、并发限流及黑白名单配置)_nginx 限流类型-CSDN博客),令牌桶(设置一个桶,当达到桶容量后其他请求等待,待前面处理完成后再进入桶中)布隆过滤器的简单限流等对接口请求次数做一个限制,甚至于hyperloglog基数统计进行限流(唯一id是key,在前面多加个统计key就能避免key重复,过期时间10秒,10秒内请求具有一次则不再请求,或者20s内达到10次进行删除缓存,封禁ip.。。)这样接口防刷就完成了;
回归主题,验证码项目如果引入了redis直接用redis就好,用全局唯一id生成的方式生成id就行,uuid,(当时觉得说个uuid太简单了,想现说个全局唯一id生成,结果没说上来还没说uuid生成原理这个,太蠢了我)
@Component("UUIDKeyGenerator") public class UUIDKeyGenerator implements CheckCodeService.KeyGenerator {@Overridepublic String generate(String prefix) {String uuid = UUID.randomUUID().toString();return prefix + uuid.replaceAll("-", "");} }
存储使用唯一id+验证码值value即可,计算式给图片生成器就行,没必要存储,最后把图片和uuid统一传给前端,后端响应先校验验证码然后匹配账号就行。