1.场景
登录或注册接口中,使用短信验证码场景时,遇到恶意调用短信接口,一毫秒多次调用,导致短信资源大幅度消耗。
2.排查
经排查发现,短信发送接口无限制,仅通过redis保留验证码发送信息。一个时间戳产生数次甚至十几次调用,多个线程同时访问redis后,取值相同,均通过验证前往调用短信接口。
3.解决方案
使用redis分布式锁,解决该问题。
相关代码如下:
3.1 redis锁实现
- public class RedisLock {
- @Autowired
- private RedisTemplate<String, String> redisTemplate;
- private static final Long NX = 1L; // 超时时间
- public boolean tryLock(String key, String value) {
- // 超时时间单位,这里发现,较新版本的RedisTemplate中,setIfAbsent方法时间单位为TimeUnit,并非大多帖子中表述的Long类型
- TimeUnit timeUnit = TimeUnit.MINUTES;
- // 这里设置redis锁超时时间为1分钟
- Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, NX, timeUnit);
- return result != null && result;
- }
- // unlock方法未测试,请测试后再使用
- public void unlock(String key, String value) {
- String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
- "return redis.call('del', KEYS[1]) " +
- "else " +
- "return 0 " +
- "end";
- redisTemplate.execute((RedisCallback<Boolean>) connection -> {
- Object nativeConnection = connection.getNativeConnection();
- if (nativeConnection instanceof Jedis) {
- return ((Jedis) nativeConnection).eval(script, Collections.singletonList(key), Collections.singletonList(value)).equals(1L);
- } else if (nativeConnection instanceof RedisClusterConnection) {
- return ((RedisClusterConnection) nativeConnection).eval(script.getBytes(), ReturnType.INTEGER, 1, key.getBytes(), value.getBytes()).equals(1L);
- }
- return false;
- });
- }
- }
复制代码 3.2 方法调用
- public boolean getVerfityCode(String mobile) {
- String smsCode = ShareCodeUtils.smsCode();
- // redis锁参数
- String lockKey = "sms_lock_" + mobile;
- String lockValue = UUID.randomUUID().toString();
- boolean send = false;
- try {
- if (redisLock.tryLock(lockKey, lockValue)) {
- // 获取锁成功,触发短信验证码功能(具体逻辑,此处因原登录逻辑需要redis支持,故保留)
- String s = redisUtils.get(RedisKeys.VERFITY_CODE + mobile);
- if (s != null) {
- // 判断验证码是否过期
- throw new ServiceException("验证码未过期,请勿重复获取");
- }
- // 发送验证码
- send = smsBaoUtil.sendSms(mobile, smsCode);
- if (send) {
- // reids 中不存在 此电话对应验证码证明已经过了 verCodeTm 秒,则可以重新生成
- redisUtils.set(RedisKeys.VERFITY_CODE + mobile, smsCode, verCodeTm);
- }
- }
- } catch (Exception e) {
- send = false;
- }
- // 很多帖子这里会增加finally解锁逻辑,这里为了保证1分钟内不会再触发短信恶意调用,取消解锁逻辑,由redis超时销毁后,自动解锁。
- return send;
- }
复制代码 到此这篇关于redis防止短信恶意调用的实现的文章就介绍到这了,更多相关redis防止短信恶意调用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
来源:https://www.jb51.net/database/335196b67.htm
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |
|