在日常中我们经常会看到有短信登录的功能,这种功能里面提供的根据手机号码发送短信的功能。
但是由于发送短信我们不能无节制的发送,如果允许无节制的发送短信,那么就会被恶人利用,形成短信轰炸,给用户造成困扰的同时,我们也会有很大的经济损失。目前各大云厂商短信审核的时候,也要求核验产品的防刷功能,因此今天我们就来演示下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秒间隔发送短信功能。最后附上代码下载:点击下载












还没有评论,来说两句吧...