Captcha.java 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. package com.huiju.estateagents.common.smsService;
  2. import com.alibaba.fastjson.JSONObject;
  3. import lombok.Data;
  4. import lombok.experimental.Accessors;
  5. import lombok.extern.slf4j.Slf4j;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.beans.factory.annotation.Value;
  8. import org.springframework.scheduling.annotation.EnableScheduling;
  9. import org.springframework.scheduling.annotation.Scheduled;
  10. import org.springframework.stereotype.Component;
  11. import java.time.LocalDateTime;
  12. import java.util.HashMap;
  13. import java.util.Hashtable;
  14. import java.util.Map;
  15. import java.util.Random;
  16. @Slf4j
  17. @Component
  18. @EnableScheduling
  19. public class Captcha {
  20. @Value("${sms-service.captcha.code}")
  21. String code;
  22. @Value("${sms-service.captcha.product}")
  23. String product;
  24. @Value("${sms-service.captcha.sign}")
  25. String sign;
  26. @Autowired
  27. Service service;
  28. // 默认超时时间, 单位秒
  29. Integer expireSec = 5 * 60;
  30. // 缓存电话列表
  31. Hashtable<String, Phone> allPhones = new Hashtable<>();
  32. /**
  33. * 发送验证码
  34. * @param tel
  35. * @param captcha
  36. * @throws Exception
  37. */
  38. public void send(String tel, String captcha) throws Exception {
  39. if (null == captcha) {
  40. throw new Exception("没有有效的验证码");
  41. }
  42. Map<String, String> content = new HashMap<>();
  43. content.put("code", captcha);
  44. content.put("product", product);
  45. Phone phone = new Phone()
  46. .setNumber(tel)
  47. .setCaptcha(captcha)
  48. .setExpire(expireTime());
  49. Service.Message message = new Service.Message()
  50. .setCode(code)
  51. .setSign(sign)
  52. .setTel(tel)
  53. .setContent(JSONObject.toJSONString(content));
  54. try {
  55. service.sendMessage(message);
  56. } catch (Exception e) {
  57. // 修改友好点的错误
  58. throw new Exception("发送验证码失败");
  59. }
  60. log.info(String.format("发送验证码 %s 到手机 %s", captcha, tel));
  61. // 发送成功, 写入缓存
  62. toCache(phone);
  63. }
  64. /**
  65. * 校验验证码
  66. * @param tel
  67. * @param captcha
  68. * @return
  69. */
  70. public boolean validate(String tel, String captcha) {
  71. String universalCode = getUniversalCode();
  72. if (universalCode.equals(captcha)) {
  73. return true;
  74. }
  75. Phone phone = fromCache(tel);
  76. if (null == phone) {
  77. return false;
  78. }
  79. if (null == captcha || !captcha.equals(phone.getCaptcha())) {
  80. return false;
  81. }
  82. // 过期则清除缓存
  83. boolean res = checkExpire(phone);
  84. if (res) {
  85. clearCache(tel);
  86. }
  87. return !res;
  88. }
  89. private String getUniversalCode() {
  90. LocalDateTime now = LocalDateTime.now();
  91. int month = now.getMonthValue();
  92. String monthStr = month > 9 ? String.valueOf(month) : "0" + String.valueOf(month);
  93. return String.valueOf(now.getYear()) + monthStr;
  94. }
  95. /**
  96. * 辅助方法: 随机生成验证码
  97. * @param length
  98. * @return
  99. */
  100. public String randCaptcha(int length) {
  101. Random random = new Random();
  102. String res = "";
  103. for (int i = 0; i < length; i ++) {
  104. int num = random.nextInt(10);
  105. res += String.valueOf(num);
  106. }
  107. return res;
  108. }
  109. /**
  110. * 定时任务 每半小时 清除一次不用的手机号
  111. */
  112. @Scheduled(fixedRate = 1000 * 60 * 30)
  113. public void clearUnused() {
  114. for (Phone phone: allPhones.values()) {
  115. if (checkExpire(phone)) {
  116. clearCache(phone.getNumber());
  117. }
  118. }
  119. }
  120. private Phone fromCache(String phone) {
  121. return allPhones.get(phone);
  122. }
  123. private void toCache(Phone phone) {
  124. allPhones.put(phone.getNumber(), phone);
  125. }
  126. private void clearCache(String key) { allPhones.remove(key); }
  127. private LocalDateTime expireTime() {
  128. return LocalDateTime.now().plusSeconds(expireSec);
  129. }
  130. // 过期返回 true
  131. private boolean checkExpire(Phone phone) {
  132. return LocalDateTime.now().isAfter(phone.getExpire());
  133. }
  134. @Data
  135. @Accessors(chain = true)
  136. public static class Phone {
  137. String number;
  138. String captcha;
  139. LocalDateTime expire;
  140. }
  141. }