在日常中我们经常会看到有短信登录的功能,这种功能里面提供的根据手机号码发送短信的功能。
但是由于发送短信我们不能无节制的发送,如果允许无节制的发送短信,那么就会被恶人利用,形成短信轰炸,给用户造成困扰的同时,我们也会有很大的经济损失。目前各大云厂商短信审核的时候,也要求核验产品的防刷功能,因此今天我们就来演示下springboot如何实现相同手机号码60秒内只能发送一次短信。
这里整体的思路就是使用注解进行自动判断,如果在时间上一条短信发送后60秒不能再进行短信的发送。同时这里的60秒间隔时间我们使用redis的缓存过期策略来实现。下面具体开始演示下。
一、新建一个springboot的项目,引入maven依赖
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.bgee.log4jdbc-log4j2</groupId> <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId> <version>${log4jdbc.log4j2.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 新增redis start --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>${jedis.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency>
二、编写一个注解,标记短信发送限制
package com.sms.demo.aop; import java.lang.annotation.*; /** * 接口防刷注解 使用: 在相应需要防刷的方法上加上 该注解,即可 * * @author: zetting * @date:2018/12/29 */ @Documented @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface SmsLimit { /** * 限制的时间值(默认是60秒) * * @return */ String value() default "60"; /** * 如果出现拦截,则需要进行提示,这里默认的提示语是:需等待60秒后才能再次发送短信 */ String message() default "需等待60秒后才能再次发送短信"; /** * 策略 * * @return */ SmsLimitStrategy strategy() default SmsLimitStrategy.DEFAULT; }
三、编写短信拦截切面,同时处理相关的逻辑
package com.sms.demo.aop; import java.lang.reflect.Method; import java.util.Base64; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import com.sms.demo.exception.SmsException; import com.sms.demo.redis.RedisUtil; import lombok.extern.slf4j.Slf4j; /** * 短信重复发送请求限制切面 */ @Aspect @Component @Slf4j public class SmsLimitAop { @Autowired private RedisUtil redisUtil; @Pointcut("@annotation(com.sms.demo.aop.SmsLimit)") public void pointcut() { } @Before("pointcut()") public void joinPoint(JoinPoint joinPoint) throws Exception { String phone = (String) joinPoint.getArgs()[0]; log.info("当前拦截的手机号码是:{},准备发送短信",phone); if (StringUtils.isEmpty(phone)) { return; } MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = joinPoint.getTarget().getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes()); SmsLimit annotation = method.getAnnotation(SmsLimit.class); String methodFullName = method.getDeclaringClass().getName() + method.getName(); entrance(annotation, phone, methodFullName); return; } private void entrance(SmsLimit limit, String phone, String methodFullName) throws Exception { SmsLimitStrategy strategy = limit.strategy(); switch (strategy) { case DEFAULT: defaultHandle(phone, limit, methodFullName); break; default: throw new SmsException("无匹配的策略"); } } private void defaultHandle(String phone, SmsLimit limit, String methodFullName) throws Exception { String base64Str = toBase64String(phone); long expire = Long.parseLong(limit.value()); String resp = redisUtil.get(methodFullName + base64Str); if (StringUtils.isEmpty(resp)) { redisUtil.set(methodFullName + base64Str, phone, expire); } else { String message = !StringUtils.isEmpty(limit.message()) ? limit.message() : "短信发送频繁,请稍后重试"; throw new SmsException(message); } } private String toBase64String(String obj) throws Exception { if (StringUtils.isEmpty(obj)) { return null; } Base64.Encoder encoder = Base64.getEncoder(); byte[] bytes = obj.getBytes("UTF-8"); return encoder.encodeToString(bytes); } }
四、编写一个Controller,添加上注解
package com.sms.demo.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.sms.demo.aop.SmsLimit; import com.sms.demo.model.BaseResponse; import lombok.extern.slf4j.Slf4j; @RestController @Slf4j public class SmsController { @RequestMapping("/sendSms") @SmsLimit(message = "60秒内不允许再次发送短信", value = "60") public BaseResponse sendSms(@RequestParam(required = true) String phone) { log.info("准备向手机号码:{} 发送短信", phone); return BaseResponse.success(); } }
备注:这里的注解我们可以直接使用默认的配置,也可以使用自定义的配置,这里我们自定义为60秒时间,并且给出60秒的提示:60秒内不允许再次发送短信
五、附上配置文件配置
server.port=9090 spring.redis.host=192.168.31.30 spring.redis.port=6379 spring.redis.password=3c0bef8420ca0961a74ff1fbe8c66ef4 spring.redis.timeout=1000 spring.redis.database=0 spring.redis.pool.max-active=10 spring.redis.pool.max-idle=8 spring.redis.pool.min-idle=2 spring.redis.pool.max-wait=100
六、运行测试下
1、请求地址:http://127.0.0.1:9090/sendSms?phone=1388888888
请求成功没有任何问题,然后我们再请求一下
就出现了错误提示,说明我们的注解实现60秒间隔发送短信功能是好使的。我们再换个手机号看下是否有错误拦截,请求:http://127.0.0.1:9090/sendSms?phone=1388888887
可以看到不同的手机号码也不会出现误拦截。因此这里是没有任何问题的。
以上就是我们springboot项目使用注解实现60秒间隔发送短信功能。最后附上代码下载:点击下载
还没有评论,来说两句吧...