publicBooleangeneratePinAndSendSms(Integer countryCode,String phoneNumber)throwsSmsException{SmsCode sms =getSmsCodeByPhoneNumber(countryCode, phoneNumber);List<Long> daySmsRecordTimes =Lists.newArrayList();if(Objects.nonNull(sms)){daySmsRecordTimes =getDaySmsRecordTimes(sms);if(!daySmsRecordTimes.isEmpty()&& daySmsRecordTimes.size()>= smsProperties.getMaxSmsCountInDay()){log.warn("PIN code to phone number '{}' was not sent because the maximum number of sends allowed by the SMS operator within 24 hours has been exceeded.",phoneNumber);thrownewSmsException(SmsException.ExceptionType.TOO_MANY);}if(sms.getType()!=null&& sms.getType()==1&& sms.getSendCount()!=null&& sms.getSendTime()!=null){checkSendingInterval(sms.getSendCount(), sms.getSendTime(), phoneNumber);}}// Generate and send the notificationString pin =generatePin();String[] parameters =newString[]{pin};Boolean sendResult =sendSmsNotification(countryCode, phoneNumber,1, parameters,true);if(!sendResult){returnfalse;}// Save PIN to DBlong timeMillis =System.currentTimeMillis();daySmsRecordTimes.add(timeMillis);Integer currentSendCount =Optional.ofNullable(sms).filter(s -> s.getSendCount()!=null).map(s -> s.getSendCount()+1).orElse(1);SmsCode smsCode =newSmsCode().setCountryCode(countryCode).setMobile(phoneNumber).setCode(pin).setType(1).setToken(null).setDaySendTimes(JSONObject.toJSONString(daySmsRecordTimes)).setSendTime(timeMillis).setSendCount(currentSendCount).setVerifyTime(null).setVerifyCount(null);if(sms ==null){smsCode.setId(IdWorker.getId());this.insert(smsCode);}else{smsCode.setId(sms.getId());this.update(smsCode);}returntrue;}publicSmsCodegetSmsCodeByPhoneNumber(Integer countryCode,String phoneNumber){returnthis.selectLimitOne(Query.of(newSmsCode().setMobile(phoneNumber).setType(1).setCountryCode(countryCode)).wrapper());}privateList<Long>getDaySmsRecordTimes(SmsCode smsCode){if(smsCode.getDaySendTimes()==null){returnLists.newArrayList();}List<Long> daySendTimes =JSONObject.parseArray(smsCode.getDaySendTimes(),Long.class);// day start timelong dayStartTime =DateUtil.beginOfDay(newDate()).getTime();List<Long> sendTimes = daySendTimes.stream().filter(s -> s > dayStartTime).sorted().collect(Collectors.toList());return sendTimes;}privatevoidcheckSendingInterval(Integer sendCount,Long sendTime,String phoneNumber)throwsSmsException{long currentTime =System.currentTimeMillis();Long sendInterval = smsProperties.getSmsSendInterval()!=null? smsProperties.getSmsSendInterval():60*1000L;// The SMS pin code can be sent only once within 50 secondsif(currentTime < sendInterval){log.warn("PIN code to phone number '{}' was not sent because the requests are too frequent. Please try again after {}.",phoneNumber, sendTime + sendInterval);thrownewSmsException(SmsException.ExceptionType.TOO_MANY);}// Wait five minutes after sending three messagesif(sendCount %3==0){if(currentTime < sendTime +5*60*1000L){log.warn("PIN code to phone number '{}' was not sent because of {} sends have blocked sending until {}",phoneNumber, sendCount, sendTime +5*60*1000L);thrownewSmsException(SmsException.ExceptionType.TOO_MANY);}}}privateStringgeneratePin(){int min =100000;int max =1000000;int randomNum = rand.nextInt((max - min))+ min;returnString.valueOf(randomNum);}
校验验证码
publicSmsVerifyResultcheckPhoneAndPin(Integer countryCode,String phoneNumber,String pin,String ipAddress)throwsSmsException{if(StringUtils.isBlank(pin)){log.warn("PIN verification for phone number '{}' was rejected because PIN is null or whitespace", phoneNumber);thrownewSmsException(SmsException.ExceptionType.NOT_FOUND);}SmsCode sms =getSmsCodeByPhoneNumber(countryCode, phoneNumber);if(sms ==null){log.info("PIN verification for phone number '{}' was rejected because verification was not found", phoneNumber);thrownewSmsException(SmsException.ExceptionType.NOT_FOUND);}if(StringUtils.isBlank(sms.getCode())){log.warn("PIN verification for phone number '{}' was rejected because pin was not found", phoneNumber);thrownewSmsException(SmsException.ExceptionType.NOT_FOUND);}if(StringUtils.isNotBlank(sms.getToken())){log.warn("PIN verification for phone number '{}' was rejected because it was already verified", phoneNumber);thrownewSmsException(SmsException.ExceptionType.CODE_VALIDATED);}long timeMillis =System.currentTimeMillis();if(sms.getSendTime()!=null&& timeMillis >(sms.getSendTime()+ smsProperties.getSmsCodeExpireTime())){log.info("PIN verification for phone number '{}' was rejected because it occurred too long after code was sent", phoneNumber);thrownewSmsException(SmsException.ExceptionType.CODE_EXPIRED);}Integer currentVerifyCount = sms.getVerifyCount()!=null? sms.getVerifyCount()+1:1;SmsCode smsCode =newSmsCode().setId(sms.getId()).setCountryCode(sms.getCountryCode()).setMobile(sms.getMobile()).setCode(sms.getCode()).setType(sms.getType()).setToken(sms.getToken()).setDaySendTimes(sms.getDaySendTimes()).setSendTime(sms.getSendTime()).setSendCount(sms.getSendCount()).setVerifyCount(currentVerifyCount).setVerifyTime(timeMillis);if(!pin.equals(sms.getCode())){updateCurrentVerifyCount(currentVerifyCount, smsCode);if(currentVerifyCount >= smsProperties.getSmsMaxVerifyCount()){log.warn("PIN verification for phone number '{}' rejected and {} failed attempts have set pin to invalid",phoneNumber, currentVerifyCount);thrownewSmsException(SmsException.ExceptionType.CODE_VALIDATE_TOO_MANY);}thrownewSmsException(SmsException.ExceptionType.CODE_INVALID);}SmsVerifyToken smsVerifyToken =newSmsVerifyToken().setCountryCode(countryCode).setPhoneNumber(phoneNumber).setIp(ipAddress);// 此处根据自己的业务情况,使用一次性字符串Token还是什么有意义的token?String token =JwtUtils.createJwt(UUID.randomUUID().toString(),JsonUtils.toJSONString(smsVerifyToken), smsProperties.getSecretKey(), smsProperties.getSmsTokenInvalidTime());smsCode.setToken(token).setCode(null).setSendCount(null).setSendTime(null);this.update(smsCode);SmsVerifyResult smsVerifyResult =newSmsVerifyResult();smsVerifyResult.setVerifyToken(smsCode.getToken());// 如果有其他信息,根据自己业务设置, 或者直接返回,token这个字符串return smsVerifyResult;}
在进行业务流程时,校验验证码Token信息
publicbooleancheckPhoneAndToken(Integer countryCode,String phoneNumber,String token){if(StringUtils.isBlank(token)){log.warn("Token verification for phone number '{}' was rejected because of an invalid token", phoneNumber);returnfalse;}SmsCode sms =getSmsCodeByPhoneNumber(countryCode, phoneNumber);if(sms ==null){log.warn("Token verification for phone number '{}' was rejected because verification was not found", phoneNumber);returnfalse;}if(!token.equals(sms.getToken())){log.warn("Token verification for phone number '{}' was rejected because the token did not match with the saved token", phoneNumber);returnfalse;}try{Claims claims =JwtUtils.parseJwt(token, smsProperties.getSecretKey());String subject = claims.getSubject();SmsVerifyToken smsVerifyToken =JSONObject.parseObject(subject,SmsVerifyToken.class);if(smsVerifyToken.getPhoneNumber()==null||!phoneNumber.equals(smsVerifyToken.getPhoneNumber())||!countryCode.equals(smsVerifyToken.getCountryCode())){log.warn("Token verification for phone number '{}' was rejected because the token did not match with the saved token", phoneNumber);returnfalse;}Date expiration = claims.getExpiration();if(expiration.before(newDate())){log.warn("Token expired");returnfalse;}}catch(Exception e){returnfalse;}// long timeMillis = System.currentTimeMillis();// if (sms.getVerifyTime() == null || timeMillis > sms.getVerifyTime() + smsProperties.getSmsTokenInvalidTime()) {// log.warn("Token verification for phone number '{}' was rejected because the token was verified too long ago", phoneNumber);// return false;// }returntrue;}