用户注册短信验证码防刷
1.使用场景
- 注册验证
- 信息变更: 修改密码、手机号等个人信息时,确保是用户本人操作,进行短信验证
- 找回密码
- 动态登录
2.防刷目的
3.防刷手段
- 前端图形验证码
- 前端滑动类
- 前端点击类
- 单个手机号请求限制
- 单个ip请求限制
- 手机号码真实性限制
4.实战操作
下面介绍一下图形验证码方式
- 引入依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.16</version>
</dependency>
-
短信配置 这里使用的是阿里云的短信服务 链接:https://market.aliyun.com/products/57000002/cmapi00046920.html?spm=5176.2020520132.101.1.14247218o2uxXD#sku=yuncode4092000001
sms:
app-code: xxxxxxxxxxxxxxxxx
template-id: M72CB42894
@ConfigurationProperties("sms")
@Component
@Data
public class SmsConfig {
private String appCode;
private String templateId;
}
@Component
@Slf4j
public class SmsComponent {
private static final String URL_TEMPLATE = "https://jmsms.market.alicloudapi.com/sms/send?mobile=%s&templateId=%s&value=%s";
@Autowired
private RestTemplate restTemplate;
@Autowired
private SmsConfig smsConfig;
public void send(String to, String templateId, String value) {
String url = String.format(URL_TEMPLATE, to, templateId, value);
HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.AUTHORIZATION, "APPCODE " + smsConfig.getAppCode());
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
log.info("url={},body={}", url, response.getBody());
if (response.getStatusCode() == HttpStatus.OK) {
log.info("发送短信成功,响应信息:{}", response.getBody());
} else {
log.error("发送短信失败,响应信息:{}", response.getBody());
}
}
}
@Data
public class SendCodeRequest {
private String captcha;
private String to;
}
public enum SendCodeEnum {
USER_REGISTER;
}
-
图形验证码接口 @Autowired
private StringRedisTemplate redisTemplate;
private static final Duration CAPTCHA_CODE_EXPIRED = Duration.ofMinutes(10);
@GetMapping("/captcha")
public void getCaptcha(HttpServletRequest request, HttpServletResponse response) {
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100, 4, 150);
try {
String code = lineCaptcha.getCode();
redisTemplate.opsForValue().set(getCaptchaKey(request), code, CAPTCHA_CODE_EXPIRED);
log.info("图形验证码code:{}", code);
lineCaptcha.write(response.getOutputStream());
} catch (IOException e) {
log.error("图形验证码出错:{}", e.getMessage());
}
}
private String getCaptchaKey(HttpServletRequest request) {
String ip = CommonUtil.getIpAddr(request);
String userAgent = request.getHeader(HttpHeaders.USER_AGENT);
String key = "zhuyz:captcha:" + CommonUtil.MD5(ip + userAgent);
return key;
}
-
短信验证码接口 @Autowired
private StringRedisTemplate redisTemplate;
@PostMapping("/send_code")
public JsonData sendCode(@RequestBody SendCodeRequest sendCodeRequest, HttpServletRequest request) {
String captchaKey = getCaptchaKey(request);
String captchaCacheValue = redisTemplate.opsForValue().get(captchaKey);
if (ObjectUtil.isNull(captchaCacheValue)
&& ObjectUtil.isNull(sendCodeRequest.getCaptcha())
&& !StrUtil.equalsIgnoreCase(captchaCacheValue, sendCodeRequest.getCaptcha())) {
return JsonData.buildResult(BizCodeEnum.CODE_CAPTCHA_ERROR);
}
redisTemplate.delete(captchaKey);
return notifyService.sendCode(SendCodeEnum.USER_REGISTER, sendCodeRequest.getTo());
}
public static final Duration CODE_EXPIRED = Duration.ofMinutes(10);
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private SmsComponent smsComponent;
@Autowired
private SmsConfig smsConfig;
@Override
public JsonData sendCode(SendCodeEnum sendCodeEnum, String to) {
String cacheCodeKey = String.format(RedisKey.CHECK_CODE_KEY, sendCodeEnum.name(), to);
String cacheCodeValue = redisTemplate.opsForValue().get(cacheCodeKey);
if (StrUtil.isNotBlank(cacheCodeValue)) {
List<String> split = StrUtil.split(cacheCodeValue, "_");
long gap = System.currentTimeMillis() - Long.parseLong(split.get(1));
if (gap < 60) {
return JsonData.buildResult(BizCodeEnum.CODE_LIMITED);
}
}
String smsCode = RandomUtil.randomNumbers(6);
String value = StrUtil.join("_", smsCode, System.currentTimeMillis());
redisTemplate.opsForValue().set(cacheCodeKey, value, CODE_EXPIRED);
if (Validator.isEmail(to)) {
} else if (Validator.isMobile(to)) {
smsComponent.send(to, smsConfig.getTemplateId(), smsCode);
}
return JsonData.buildSuccess();
}
|