Browse Source

优化验证码短信发送逻辑

woody 1 year ago
parent
commit
3a95b64f0e

+ 16 - 8
framework-sms/src/main/java/com/chelvc/framework/sms/support/SwitchableCaptchaSmsHandler.java

@@ -82,12 +82,18 @@ public class SwitchableCaptchaSmsHandler implements CaptchaSmsHandler {
 
     @Override
     public SmsSession send(@NonNull String mobile) {
+        // 短信验证码发送频率限制
+        String lock = "sms:captcha:interval:" + mobile;
+        String secret = RedisUtils.tryLock(lock, Duration.ofSeconds(this.properties.getInterval()));
+        if (StringUtils.isEmpty(secret)) {
+            throw new ResourceUnavailableException(TemplateSmsHandler.SMS_SEND_FREQUENTLY, "短信发送过于频繁,请稍后再试");
+        }
+
         // 如果目标手机号属于测试手机号,则直接返回配置的验证码信息,否则获取真实的验证码
-        String test = this.lookupTestingCaptcha(mobile);
-        boolean testing = StringUtils.nonEmpty(test);
-        String code = testing ? test : String.valueOf(ThreadLocalRandom.current().nextInt(100000, 1000000));
-        Duration duration = Duration.ofSeconds(this.properties.getInterval());
-        return RedisUtils.tryLockAround("sms:interval:" + mobile, duration, () -> {
+        try {
+            String test = this.lookupTestingCaptcha(mobile);
+            boolean testing = StringUtils.nonEmpty(test);
+            String code = testing ? test : String.valueOf(ThreadLocalRandom.current().nextInt(100000, 1000000));
             String key = this.key(mobile), token = StringUtils.uuid();
             Captcha captcha = Captcha.builder().token(token).code(code).mobile(mobile).build();
             if (!testing) {
@@ -95,9 +101,11 @@ public class SwitchableCaptchaSmsHandler implements CaptchaSmsHandler {
             }
             this.redisTemplate.opsForValue().set(key, captcha, this.properties.getExpiration(), TimeUnit.SECONDS);
             return SmsSession.builder().token(token).expiration(this.properties.getExpiration()).build();
-        }, () -> {
-            throw new ResourceUnavailableException(TemplateSmsHandler.SMS_SEND_FREQUENTLY, "短信发送过于频繁,请稍后再试");
-        });
+        } catch (RuntimeException e) {
+            // 保证验证码发送失败场景下及时释放锁
+            RedisUtils.unlock(lock, secret);
+            throw e;
+        }
     }
 
     @Override