由于业务需要,需要使用第三方短信平台,进行验证码的发送。网上的短信服务平台主要由:百度、腾讯、阿里云;采用官方提供的SDK,调用接口即可。
腾讯云短信服务平台和阿里云短信服务平台,一般步骤为:注册--->企业/个人 实名认证----->创建应用----->创建短信签名------>创建模板---->等待审核---->使用提供的SDK调用接口,发送短信。
我使用的是腾讯云短信服务平台,阿里云的我进去之后和他的流程类似,这里直说腾讯云短信服务平台,注册完之后进去,首先会提示一个友好的提示界面,然后点击快速开始即可:
安装提示,一步一步会进入到认证界面,认证分为个人和企业,这里是给公司使用,我选择的是企业认证,然后选择已经完成企业认证的微信公众号认证(其他认证方式也阔以):
认证成功后,腾讯云短信平台会提示您的权限信息等,然后点击确定即可:
进行到这里,首先应该点击【应用管理】创建应用,这里使用默认的应用(系统给默认创建了一个,也可以自己创建);这里我使用默认的,所以,直接从【快速入门】,进行签名,模板的配置,然后等待审核结果,等审核通过,即可使用官方SDK调用API接口,发送短信:
点击应用可以查看应用的详细信息,
其中SDK AppID,App Key 是需要你保密的,存放在你的服务器后台,调用短信API接口时,需要用到。
完成以上配置之后,下面就可以进行SpringBoot工程集成腾讯云短信SDK调用短信接口,发送验证码短信了。
第一步:pom.xml中,引入SDK
<!-- 腾讯云sdk-->
<dependency>
<groupId>com.github.qcloudsms</groupId>
<artifactId>qcloudsms</artifactId>
<version>1.0.6</version>
</dependency>
第二步:创建短信验证码发送工具类
package com.inspur.tax.api.sms.utils;
import com.alibaba.fastjson.JSONObject;
import com.github.qcloudsms.SmsSingleSender;
import com.github.qcloudsms.SmsSingleSenderResult;
import com.github.qcloudsms.httpclient.HTTPException;
import com.sun.org.apache.xpath.internal.objects.XObject;
import com.inspur.tax.api.sms.cache.CacheManagerImpl;
import org.json.JSONException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
public class SmsUtils {
// 短信应用 SDK AppID
private static int appid = 140******; // SDK AppID 以1400开头
// 短信应用 SDK AppKey
private static String appkey = "37b63******************3104399";
// 短信模板 ID,需要在短信应用中申请
private static int templateId = 6******; // NOTE: 这里的模板 ID`7839`只是示例,真实的模板 ID 需要在短信控制台中申请
// 签名
private static String smsSign = "中********平台"; // NOTE: 签名参数使用的是`签名内容`,而不是`签名ID`。这里的签名"腾讯云"只是示例,真实的签名需要在短信控制台申请
static CacheManagerImpl cacheManagerImpl = new CacheManagerImpl();
/**
* 随机生成6位随机验证码
* 方法说明
* @Discription:扩展说明
* @return
* @return String
*/
public static String createRandomVcode(){
//验证码
String vcode = "";
for (int i = 0; i < 6; i++) {
vcode = vcode + (int)(Math.random() * 9);
}
return vcode;
}
/*
*发送手机验证码(30分钟)
*/
public static boolean sendSms(String phoneNumber){
try {
String verifyCode = createRandomVcode();
String[] params = {verifyCode,"10"};
SmsSingleSender ssender = new SmsSingleSender(appid, appkey);
SmsSingleSenderResult result = ssender.sendWithParam("86", phoneNumber,
templateId, params, smsSign, "", "");
System.out.println(result);
if (result.result == 0) {
//放入缓存 timeout为0一直保存
cacheManagerImpl.putCache(phoneNumber, verifyCode + System.currentTimeMillis(), 0);
return true;
} else {
return false;
}
} catch (HTTPException e) {
// HTTP 响应码错误
e.printStackTrace();
return false;
} catch (JSONException e) {
// JSON 解析错误
e.printStackTrace();
return false;
} catch (IOException e) {
// 网络 IO 错误
e.printStackTrace();
return false;
}
}
public static Map<String, Object> checkSms(String verifyCode, String phoneNumber) {
Map<String, Object> result = new HashMap<String, Object>();
String rtnCode = "";
String rtnMsg = "";
try{
String verify = cacheManagerImpl.getCacheByKey(phoneNumber).getDatas().toString();
if(!verify.substring(0,6).equals(verifyCode)){
rtnCode = "-1";
rtnMsg = "验证码错误";
result.put("rtnCode", rtnCode);
result.put("rtnMsg", rtnMsg);
return result;
}
if((Double.parseDouble(verifyCode+System.currentTimeMillis()) - Double.parseDouble(verify)) > 1000*60*10){
rtnCode = "-2";
rtnMsg = "验证码过期";
result.put("rtnCode", rtnCode);
result.put("rtnMsg", rtnMsg);
cacheManagerImpl.clearByKey(phoneNumber);
return result;
}
}catch (NullPointerException e){
rtnCode = "-1";
rtnMsg = "手机号错误或验证码已被使用或验证码已过期移除";
result.put("rtnCode", rtnCode);
result.put("rtnMsg", rtnMsg);
return result;
}
rtnCode = "0";
rtnMsg = "验证通过";
result.put("rtnCode", rtnCode);
result.put("rtnMsg", rtnMsg);
cacheManagerImpl.clearByKey(phoneNumber);
return result;
}
}
这里将发送给用户的短信验证码报错在缓存中,来进行验证码的验证,当然也可以将验证码报存在Session或者数据库中。
定义缓存操作接口,ICacheManager.java
package com.inspur.tax.api.sms.cache;
import java.util.Map;
import java.util.Set;
/*定义缓存操作接口,ICacheManager.java*/
public interface ICacheManager {
/**
* 存入缓存
* @param key
* @param cache
*/
void putCache(String key, EntityCache cache);
/**
* 存入缓存
* @param key
* @param
*/
void putCache(String key, Object datas, long timeOut);
/**
* 获取对应缓存
* @param key
* @return
*/
EntityCache getCacheByKey(String key);
/**
* 获取对应缓存
* @param key
* @return
*/
Object getCacheDataByKey(String key);
/**
* 获取所有缓存
* @param
* @return
*/
Map<String, EntityCache> getCacheAll();
/**
* 判断是否在缓存中
* @param key
* @return
*/
boolean isContains(String key);
/**
* 清除所有缓存
*/
void clearAll();
/**
* 清除对应缓存
* @param key
*/
void clearByKey(String key);
/**
* 缓存是否超时失效
* @param key
* @return
*/
boolean isTimeOut(String key);
/**
* 获取所有key
* @return
*/
Set<String> getAllKeys();
}
缓存类CacheManagerImpl
package com.inspur.tax.api.sms.cache;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/*实现接口ICacheManager,CacheManagerImpl.java
这里我使用了ConcurrentHashMap来保存缓存,本来以为这样就是线程安全的,
其实不然,在后面的测试中会发现它并不是线程安全的。*/
public class CacheManagerImpl implements ICacheManager {
private static Map<String, EntityCache> caches = new ConcurrentHashMap<String, EntityCache>();
/**
* 存入缓存
* @param key
* @param cache
*/
public void putCache(String key, EntityCache cache) {
caches.put(key, cache);
}
/**
* 存入缓存
* @param key
* @param
*/
public void putCache(String key, Object datas, long timeOut) {
timeOut = timeOut > 0 ? timeOut : 0L;
putCache(key, new EntityCache(datas, timeOut, System.currentTimeMillis()));
}
/**
* 获取对应缓存
* @param key
* @return
*/
public EntityCache getCacheByKey(String key) {
if (this.isContains(key)) {
return caches.get(key);
}
return null;
}
/**
* 获取对应缓存
* @param key
* @return
*/
public Object getCacheDataByKey(String key) {
if (this.isContains(key)) {
return caches.get(key).getDatas();
}
return null;
}
/**
* 获取所有缓存
* @param
* @return
*/
public Map<String, EntityCache> getCacheAll() {
return caches;
}
/**
* 判断是否在缓存中
* @param key
* @return
*/
public boolean isContains(String key) {
return caches.containsKey(key);
}
/**
* 清除所有缓存
*/
public void clearAll() {
caches.clear();
}
/**
* 清除对应缓存
* @param key
*/
public void clearByKey(String key) {
if (this.isContains(key)) {
caches.remove(key);
}
}
/**
* 缓存是否超时失效
* @param key
* @return
*/
public boolean isTimeOut(String key) {
if (!caches.containsKey(key)) {
return true;
}
EntityCache cache = caches.get(key);
long timeOut = cache.getTimeOut();
long lastRefreshTime = cache.getLastRefeshTime();
if (timeOut == 0 || System.currentTimeMillis() - lastRefreshTime >= timeOut) {
return true;
}
return false;
}
/**
* 获取所有key
* @return
*/
public Set<String> getAllKeys() {
return caches.keySet();
}
}
创建缓存对象EntityCache.java
package com.inspur.tax.api.sms.cache;
/*创建缓存对象EntityCache.java*/
public class EntityCache {
/**
* 保存的数据
*/
private Object datas;
/**
* 设置数据失效时间,为0表示永不失效
*/
private long timeOut;
/**
* 最后刷新时间
*/
private long lastRefeshTime;
public EntityCache(Object datas, long timeOut, long lastRefeshTime) {
this.datas = datas;
this.timeOut = timeOut;
this.lastRefeshTime = lastRefeshTime;
}
public Object getDatas() {
return datas;
}
public void setDatas(Object datas) {
this.datas = datas;
}
public long getTimeOut() {
return timeOut;
}
public void setTimeOut(long timeOut) {
this.timeOut = timeOut;
}
public long getLastRefeshTime() {
return lastRefeshTime;
}
public void setLastRefeshTime(long lastRefeshTime) {
this.lastRefeshTime = lastRefeshTime;
}
}
具体实现Service类:
package com.inspur.tax.api.sms.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.inspur.tax.api.common.Result;
import com.inspur.tax.api.common.Ywlx;
import com.inspur.tax.api.common.entity.TransEntity;
import com.inspur.tax.api.sms.cache.CacheManagerImpl;
import com.inspur.tax.api.sms.service.ISmsSendService;
import com.inspur.tax.api.sms.utils.SmsUtils;
import com.inspur.tax.constat.ResultEnum;
import com.inspur.tax.exception.JkjhException;
import com.inspur.tax.utils.FastJsonUtils;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
@Service
public class SmsSendServiceImpl implements ISmsSendService{
@Override
public TransEntity<?> process(TransEntity<?> trans) {
Map<String,Object> business = new HashMap<String,Object>();
try {
String ywlx = trans.getComment().getYwlx();
Map<String,Object> param = FastJsonUtils.stringToCollect(String.valueOf(trans.getBusiness()));
if(Ywlx.SMS_SEND.getCode().equals(ywlx)) {//发送短信验证码
business = sendSms(param);
}else if(Ywlx.SMS_CHECK.getCode().equals(ywlx)) {//验证短信验证码
business = checkSms(param);
}
trans = TransEntity.setResult(ResultEnum.YHZX_success).change(trans).data(business);
}catch(Exception e) {
throw new JkjhException(Result.SYS_ERROR,trans);
}finally {
return trans;
}
}
@Override
public boolean isSupport(String ywlx) {
// TODO Auto-generated method stub
return (Ywlx.SMS_SEND.getCode().equals(ywlx) ||
Ywlx.SMS_CHECK.getCode().equals(ywlx) );
}
public Map<String, Object> sendSms(Map<String, Object> params){
Map<String, Object> result = new HashMap<String, Object>();
String rtnCode = "";
String rtnMsg = "";
String phoneNumber = String.valueOf(params.get("phoneNumber"));
boolean bool = SmsUtils.sendSms(phoneNumber);
if(bool){
rtnCode = "0";
rtnMsg = "验证码发送成功";
} else {
rtnCode = "-3";
rtnMsg = "验证码发送失败";
}
result.put("rtnCode", rtnCode);
result.put("rtnMsg", rtnMsg);
return result;
}
public Map<String, Object> checkSms( Map<String, Object> params){
String phoneNumber = String.valueOf(params.get("phoneNumber"));
String verifyCode = String.valueOf(params.get("verifyCode"));
Map<String, Object> result = SmsUtils.checkSms(verifyCode, phoneNumber);
return result;
}
}
参考博客:https://blog.csdn.net/weixin_41843757/article/details/100141677