document
API test

发送手机验证码

POST

Description or Example

# BUG ## feign调用微服务的时候总是405 > 这是因为参数污染的问题, ~~405一定和参数有关~~, 而且这个参数必要转为json, 这就考虑参数污染, 可以考虑加上@RequestParam > 注意加入Redis配置和依赖 # 核心源码 ```java @Controller public class LoginRegisterController { @Autowired private SMSService smsService; @GetMapping("/sms/sendCode") @ResponseBody public R getPhoneCode(String phone) { return smsService.sendPhoneSMS(phone); } } ``` ```java @FeignClient("bitmall-thirdpart") public interface SMSService { @RequestMapping("/thirdpart/sms/sendCode") R sendPhoneSMS(@RequestParam("phone") String phone); } ``` ```java @RestController @RequestMapping("thirdpart/sms") public class SMSController { @Autowired private StringRedisTemplate redisTemplate; @Autowired private SMSComponent smsComponent; /** * 给指定的手机号发送纯数字验证码 * @param phone 接受验证码的手机号 * @return */ @RequestMapping("/sendCode") public R sendPhoneSMS(@RequestParam("phone") String phone) { // 1. 获取Redis里面的缓存起来的验证码 String key = PHONE_SMS_CODE + phone; String code = redisTemplate.opsForValue().get(key); // 判断验证码是否存在 if (StringUtils.isNotBlank(code)) { // 继续判断是否超过防刷时限 Long createTimeMillis = Long.valueOf(code.split("_")[1]); Long curTimeMillis = System.currentTimeMillis(); long minutes = (curTimeMillis - createTimeMillis) / 60000; // 距今多少分钟 // 这里做了一个规定, 三分钟内只能获取一个验证码 if (minutes < 4) { return R.error(SMS_FAILURE.getCode(), SMS_FAILURE.getMessage()); } } // 到这里说明没有获取过验证码或者验证码需要再次获取 code = RandomUtil.randomNumbers(6); // 保存到redis // 过期时间默认是15分钟 redisTemplate.opsForValue().set(key, code + "_" + System.currentTimeMillis(), 15, TimeUnit.MINUTES); try { smsComponent.sendPhoneCode(phone, code); } catch (ExecutionException | InterruptedException e) { return R.error(SMS_GET_FAILURE.getCode(), SMS_GET_FAILURE.getMessage()); } return R.ok(); } } ``` # 知识点 ## 为什么给后端发请求, 而不是像OSS一样直接发请求给ailyunSMS服务器? ### 旧版API > 对于旧版API而言, 如果直接给SMS服务器发送请求, 有一个致命的缺陷, 即请求头必须要有用户的APIKEY, 如果有不法分子拿到了APIKEY, 就可以根据官方文档猛发请求, 消耗公司的验证码, 造成财产损失 ### 新版API > 当前我用的接口架构不支持获取签名, 必须拿到用户的ACCESSID和SEUCRKEY才行, 这些给前端发送, 极大几率被截获, 因此, 为了安全, 统一由后端发请求 即便牺牲一定的性能 ### 架构图 [架构图](https://www.processon.com/embed/64dca3c9039d400ab5105bb7) ## 为什么验证码存储在Reids里面? 1. 因为验证码具有时效性, 即验证码不可能永久有效的, 因此不需要持久化, 放在Redis加上过期时间即可 2. 放在Redis里面解决分布式下会话不共享问题 ## 为什么给每一个code加上一个时间戳? > 因为前端的缘故, 页面很有可能被扒, 里面的请求验证码的地址暴露, 一旦这样, 如果有心之人频繁发该请求, 会大量消耗验证码, 造成财产损失, 因此加上时间戳, 判断是否在3min内, 若在则不让其获取, 保证财产安全 ## 如何实现防刷和限时校验 > 防刷可以通过加上时间戳, 通过当前时间戳和存储起来的时间戳比较, 从来防刷 > 限时校验可以给Redis的缓存加上过期时间, 若存在说明code还在有效期, 可以校验, 否则无法校验 > 注意: 防刷时间一定要比过期时间短, 否则过期了, 防刷时间没了, 防刷没意义了 ## 架构 > 整体的架构是, 先调用auth微服务的获取验证码controller, 然后该controller调用第三方微服务获取验证码