注册邮箱验证码功能开发
前文提示
开发流程
工具类
public class CommonUtil {
/**
* 获取ip
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request) {
String ipAddress = null;
try {
ipAddress = request.getHeader("x-forwarded-for");
if(ipAddress == null || ipAddress.length() == 0 || "unknow".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if(ipAddress.equals("127.0.0.1")) {
// 根据网卡取本机配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress = inet.getHostAddress();
}
}
// 对于通过多个代理的情况, 第一个IP为客户端真实IP, 多个IP按照‘,’分割
if(ipAddress != null && ipAddress.length() > 15) {
// "***.***.***.***".length() = 15
if(ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
} catch (Exception e){
ipAddress = "";
}
return ipAddress;
}
/**
* MD5加密
* @param data
* @return
*/
public static String MD5(String data) {
try {
java.security.MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for(byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
} catch (Exception exception) {
}
return null;
}
/**
* 获取验证码随机数
* @param length
* @return
*/
public static String getRandomCode(int length) {
String sources = "0123456789";
Random random = new Random();
StringBuilder sb = new StringBuilder();
for(int j = 0; j < length; j++) {
sb.append(sources.charAt(random.nextInt(9)));
}
return sb.toString();
}
/**
* 获取当前时间戳
* @return
*/
public static long getCurrentTimestamp() {
return System.currentTimeMillis();
}
}
邮箱,手机正则校验
public class CheckUtil {
/**
* 邮箱正则
*/
private static final Pattern MAIL_PATTERN = Pattern.compile("^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$");
/**
* 手机号正则,暂时未用
*/
private static final Pattern PHONE_PATTERN = Pattern.compile("^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$");
/**
* @param email
* @return
*/
public static boolean isEmail(String email) {
if (null == email || "".equals(email)) {
return false;
}
Matcher m = MAIL_PATTERN.matcher(email);
return m.matches();
}
/**
* 暂时未用
* @param phone
* @return
*/
public static boolean isPhone(String phone) {
if (null == phone || "".equals(phone)) {
return false;
}
Matcher m = PHONE_PATTERN.matcher(phone);
return m.matches();
}
}
接口使用的枚举类
public enum SendCodeEnum {
/**
* 用户注册
*/
USER_REGISTER;
}
redis 缓存 key (基于原来的 key 拼装时间戳)
public class CacheKey {
/**
* 注册验证码,第一个是类型,第二个是接收号码
*/
public static final String CHECK_CODE_KEY = "code:%s:%s";
}
Service
public interface NotifyService {
/**
* 发送注册码
* @param sendCodeEnum
* @param to
* @return
*/
JsonData sendCode(SendCodeEnum sendCodeEnum, String to);
}
SerivceImpl
@Service
@Slf4j
public class NotifyServiceImpl implements NotifyService {
@Autowired
private MailService mailService;
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 邮箱验证码有效期10分钟
*/
private static final long CODE_EXPIRED = 60 * 1000 * 1;
// 验证码的标题
private static final String SUBJECT = "1024shop验证码";
// 验证码内容
private static final String CONTENT = "您的验证码是%s, 有效时间是60秒。";
@Override
public JsonData sendCode(SendCodeEnum sendCodeEnum, String to) {
String cacheKey = String.format(CacheKey.CHECK_CODE_KEY, sendCodeEnum, to);
String cacheValue = redisTemplate.opsForValue().get(cacheKey);
// 如果不为空,则判断是60秒内重复发送
if(StringUtils.isNotBlank(cacheValue)) {
long ttl = Long.parseLong(cacheValue.split("_")[1]);
// 当前时间戳 - 验证码发送时间戳, 如果小于60秒, 则不给重复发送
if(CommonUtil.getCurrentTimestamp() - ttl < 1000 * 60) {
log.info("重复发送验证码, 时间间隔:{} 秒", (CommonUtil.getCurrentTimestamp() - ttl) / 1000);
return JsonData.buildResult(BizCodeEnum.CODE_LIMITED);
}
}
// 拼接验证码 2323_2323232323232323
String code = CommonUtil.getRandomCode(6);
String value = code + "_" + CommonUtil.getCurrentTimestamp();
// 设置超时时间为1分钟
redisTemplate.opsForValue().set(cacheKey, value, CODE_EXPIRED, TimeUnit.MILLISECONDS);
// 验证邮箱格式
if(CheckUtil.isEmail(to)) {
// 邮箱验证码
mailService.sendMail(to, SUBJECT, String.format(CONTENT, code));
return JsonData.buildSuccess();
} else if(CheckUtil.isPhone(to)) {
// 短信验证码
}
return JsonData.buildResult(BizCodeEnum.CODE_TO_ERROR);
}
}
Controller
@Api(tags = "通知模块")
@RestController
@RequestMapping("/api/user/v1")
@Slf4j
public class NotifyController {
@Autowired
private Producer captchaProducer;
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private NotifyService notifyService;
/**
* 图形验证码有效期10分钟
*/
private static final long CAPTCHA_CODE_EXPIRED = 60 * 1000 * 10;
/**
* 获取图形验证码
* @param request
* @param response
*/
@ApiOperation("获取图形验证码")
@GetMapping("captcha")
public void getCaptcha(HttpServletRequest request, HttpServletResponse response){
String captchaText = captchaProducer.createText();
log.info("图形验证码:{}",captchaText);
//存储, 设置一分钟后失效
redisTemplate.opsForValue().set(getCaptchaKey(request),captchaText,CAPTCHA_CODE_EXPIRED, TimeUnit.MILLISECONDS);
BufferedImage bufferedImage = captchaProducer.createImage(captchaText);
ServletOutputStream outputStream = null;
try {
outputStream = response.getOutputStream();
ImageIO.write(bufferedImage,"jpg",outputStream);
outputStream.flush();
outputStream.close();
} catch (IOException e) {
log.error("获取图形验证码异常:{}",e);
}
}
@ApiOperation("发送邮箱注册验证码")
@GetMapping("send_code")
public JsonData sendRegisterCode(@RequestParam(value = "to", required = true) String to,
@RequestParam(value = "captcha", required = true) String captcha,
HttpServletRequest request) {
String key = getCaptchaKey(request);
String cacheCaptcha = redisTemplate.opsForValue().get(key);
// 校验图形验证码是否一样
if(captcha != null && cacheCaptcha != null && captcha.equalsIgnoreCase(cacheCaptcha)) {
// 成功
redisTemplate.delete(key);
JsonData jsonData = notifyService.sendCode(SendCodeEnum.USER_REGISTER, to);
return jsonData;
} else {
return JsonData.buildResult(BizCodeEnum.CODE_CAPTCHA_ERROR);
}
}
/**
* 获取缓存的验证码
* @param request
* @return
*/
private static String getCaptchaKey(HttpServletRequest request) {
String ip = CommonUtil.getIpAddr(request);
String userAgent = request.getHeader("User-Agent");
String key = "user-service:captcha:" + CommonUtil.MD5(ip + userAgent);
log.info("ip = {}", ip);
log.info("userAgent = {}", userAgent);
log.info("key = {}", key);
return key;
}
}
吾生也有涯,而知也无涯。