✅P214_商城业务-认证服务-验证码防刷校验
三方服务短信发送接口
编写短信验证接口,方便其它服务调用。
cfmall-third-party/src/main/java/com/gyz/cfmall/thirdparty/controller/SmsController.java
package com.gyz.cfmall.thirdparty.controller;
import com.gyz.cfmall.thirdparty.component.SmsComponent;
import com.gyz.common.utils.R;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.annotation.Resource;
@Controller
@RequestMapping(value = "/sms")
public class SmsController {
@Resource
private SmsComponent smsComponent;
/**
* 提供给别的服务进行调用
*
* @param phone
* @param code
* @return
*/
@GetMapping(value = "/sendCode")
public R sendCode(@RequestParam("phone") String phone, @RequestParam("code") String code) {
//发送验证码
smsComponent.sendCode(phone, code);
return R.ok();
}
}
认证服务远程调用发送短信接口
开启远程服务调用功能注解:
@EnableFeignClients
认证服务中编写获取短信验证码的controller
@RestController
public class LoginController {
@Resource
private ThirdPartFeignService thirdPartFeignService;
@GetMapping(value = "/sms/sendCode")
public R sendCode(@RequestParam("phone") String phone) {
String code = UUID.randomUUID().toString().substring(0, 5);
thirdPartFeignService.sendCode(phone, code);
return R.ok();
}
}
远程调用接口编写:
cfmall-auth-server/src/main/java/com/gyz/cfmall/feign/ThirdPartFeignService.java
package com.gyz.cfmall.feign;
import com.gyz.common.utils.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient("cfmall-third-party")
public interface ThirdPartFeignService {
@GetMapping(value = "/sms/sendCode")
R sendCode(@RequestParam("phone") String phone, @RequestParam("code") String code);
}
实现注册页面发送验证码
验证码请求页面修改
cfmall-auth-server/src/main/resources/templates/reg.html
为手机号码input框设置id,方便获取
将 for="userName"
删除,增加 id="phoneNum"
设置密码输入框设置name属性取值
获取输入框验证码
发送请求,请求后台发送短信验证码
防刷校验思路
问题:有人拿到请求路径恶意请求获取验证码,如何防止一个手机号码限制时间内多次获取短信验证码?
解决思路:
- 将短信验证码存储在redis中,key为
phoneNum
,value为:验证码+存储时系统的当前时间
; - 从redis中查询为null,则调用接口发送短信验证码,若查询不为空则判断是否超过60s,是则再次调用发送短信验证码,否则返回提示信息。
实现步骤
1、导入redis依赖,并配置好redis地址,端口号
cfmall-auth-server/pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、编写验证码前置信息
cfmall-common/src/main/java/com/gyz/common/constant/AuthServerConstant.java
public class AuthServerConstant {
public static final String SMS_CODE_CACHE_PREFIX = "sms:code:";
}
3、异常枚举
cfmall-common/src/main/java/com/gyz/common/exception/BizCodeEnum.java
public enum BizCodeEnum {
SMS_CODE_EXCEPTION(10002,"验证码获取频率太高,请稍后再试"),
//省略代码...
;
}
4、接口编写
cfmall-auth-server/src/main/java/com/gyz/cfmall/controller/LoginController.java
package com.gyz.cfmall.controller;
import com.gyz.cfmall.feign.ThirdPartFeignService;
import com.gyz.common.constant.AuthServerConstant;
import com.gyz.common.exception.BizCodeEnum;
import com.gyz.common.utils.R;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.annotation.Resource;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* @author gong_yz
* @Description
*/
@Controller
public class LoginController {
private static final Logger log = LoggerFactory.getLogger(LoginController.class);
@Resource
private ThirdPartFeignService thirdPartFeignService;
@Resource
private StringRedisTemplate stringRedisTemplate;
@GetMapping(value = "/sms/sendCode")
public R sendCode(@RequestParam("phone") String phone) {
// 处理一个手机号码60s内多次获取短信验证码问题
String s = stringRedisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone);
if (StringUtils.isNotEmpty(s)) {
//活动存入redis的时间,用当前时间减去存入redis的时间,判断用户手机号是否在60s内发送验证码
Long currentTime = Long.parseLong(s.split("_")[1]);
// 单位为毫秒,因此,60秒要转化为毫秒即60000
if (currentTime - System.currentTimeMillis() < 60000) {
return R.error(BizCodeEnum.SMS_CODE_EXCEPTION.getCode(), BizCodeEnum.SMS_CODE_EXCEPTION.getMessage());
}
}
String code = UUID.randomUUID().toString().substring(0, 5);
stringRedisTemplate.opsForValue().set(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone, code + "_" + System.currentTimeMillis(), 10, TimeUnit.MINUTES);
thirdPartFeignService.sendCode(phone, code);
return R.ok();
}
}
5、注册页面的请求发送验证码的回调函数编写
cfmall-auth-server/src/main/resources/templates/reg.html
// 验证码倒计时60s
$(function () {
$("#sendCode").click(function () {
//2、倒计时
if ($(this).hasClass("disabled")) {
//正在倒计时中
} else {
//1、给指定手机号发送验证码
$.get("/sms/sendCode?phone=" + $("#phoneNum").val(),function (data) {
if (data.code!=0){
alert(data.msg);
}
});
timeoutChangeStyle();
}
});
});