短信服务现在在很多地方都用的到,我们最多接触到的可能就是验证码的发送了;现在很多大厂都提供由短信服务对应的api,直接调用就可以轻松实现短信发送;下面我以阿里云为例,演示一下如何短信发送验证码;
首先是一些准备工作,注册阿里云账号就不说了;
打开自己的控制台,搜索找到短信服务:
点开后找到快速学习(现在阿里云短信服务正式使用开通需要营业执照什么的证明,但是它也提供了学习用的方法,实现起来都差不多):
在这里可以绑定一下自己的手机号,因为用来测试,所以只有绑定的手机号才能成功接收短信:
然后可以看到下面有个签名模板,这个是一些必要的参数,一会要用到:
这些都弄完后还有两个以外的步骤:
记住保存好自己的AccessKey ID和secret;
做完这些后,准备工作就完成了,可以发一条短信测试一下:
下面就是代码开发;
其实阿里云已经提供好代码模板了,能力强的可以直接看它的模板,并不难:
这里代码有两种版本,我这里就用网上最多的老版的了(还是看需求,在java这一块老版本其实没有问题的);
下面就是具体代码开发:
先引入依赖:
<dependency>
<groupId>com.aliyungroupId>
<artifactId>aliyun-java-sdk-dysmsapiartifactId>
<version>2.2.1version>
dependency>
<dependency>
<groupId>com.aliyungroupId>
<artifactId>aliyun-java-sdk-coreartifactId>
<version>4.2.0version>
dependency>
在application.yml中配置一些基本信息:
region_id就是服务地区,选择离自己近的就好;
另外两个参数就是刚才创建的accesskey;
然后创建一个工具类用来获取这三个参数:
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class AliyunConstant implements InitializingBean {
@Value("aliyun.sms.region_id")
private String regionId;
@Value("aliyun.sms.access_key_id")
private String accessKeyId;
@Value("aliyun.sms.secret")
private String secret;
public static String REGION_ID;
public static String ACCESS_KEY_ID;
public static String SECRET;
@Override
public void afterPropertiesSet() throws Exception {
REGION_ID = regionId;
ACCESS_KEY_ID = accessKeyId;
SECRET = secret;
}
}
然后配置短信发送模板:
/**
* 短信发送方法
* @param map 短信内容
* @param phone 发送电话号码
* @return 发送信息结果
*/
public Boolean sendMessage(Map<String, Object> map, String phone) {
if (StringUtils.isAnyBlank(phone)) {
throw new BusinessException(StatusCode.NULL_ERROR, "手机号为空");
}
DefaultProfile profile =
DefaultProfile.getProfile(AliyunConstant.REGION_ID, AliyunConstant.ACCESS_KEY_ID, AliyunConstant.SECRET);
IAcsClient client = new DefaultAcsClient(profile);
SendSmsRequest request = new SendSmsRequest();
request.setSignName("阿里云短信测试"); // 短信签名
request.setTemplateCode("SMS_154950909"); // 短信模板code
request.setPhoneNumbers(phone); // 测试号的号码(其它号码需要添加测试号先)
request.setTemplateParam(JSONObject.toJSONString(map)); // 发送短信信息内容(因为这里是验证码,格式为:{"code":"4-6位数字"})
try {
SendSmsResponse response = client.getAcsResponse(request);
log.info(JSON.toJSONString(response));
return response.getMessage().equals("OK"); // 短信发送成功时Message信息为OK(默认的)
} catch (ServerException e) {
e.printStackTrace();
} catch (ClientException e) {
log.error("ErrCode:" + e.getErrCode());
log.error("ErrMsg:" + e.getErrMsg());
log.error("RequestId:" + e.getRequestId());
}
return false;
}
注意:这里是验证码发送,所以map格式需要为:{“code”:“4-6位数字”},格式错误会发送失败;
在这里我在多说点验证码发送的细节吧;
首先验证码的获取可以通过后端生成一个随机数,这里提供一个生成四位或六位的随机数工具类:
import lombok.extern.slf4j.Slf4j;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
/**
* 生成随机短信验证码
*/
@Slf4j
public class RandomUtils {
private static final Random random = new Random();
private static final DecimalFormat fourdf = new DecimalFormat("0000");
private static final DecimalFormat sixdf = new DecimalFormat("000000");
/**
* 获取四位随机验证码
*/
public static String getFourBitRandom() {
return fourdf.format(random.nextInt(10000));
}
/**
* 获取六位随机验证码
*/
public static String getSixBitRandom() {
return sixdf.format(random.nextInt(1000000));
}
/**
* 给定数组,抽取n个数据
*/
public static ArrayList getRandom(List list, int n) {
Random random = new Random();
HashMap<Object, Object> hashMap = new HashMap<Object, Object>();
// 生成随机数字并存入HashMap
for (int i = 0; i < list.size(); i++) {
int number = random.nextInt(100) + 1;
hashMap.put(number, i);
}
// 从HashMap导入数组
Object[] robjs = hashMap.values().toArray();
ArrayList r = new ArrayList();
// 遍历数组并打印数据
for (int i = 0; i < n; i++) {
r.add(list.get((int) robjs[i]));
log.info(list.get((int) robjs[i]) + "\t");
}
log.info("\n");
return r;
}
}
然后通过该工具类生成验证码,然后就可以调用上面的短信发送模板,把验证码和发送给的手机号传过去;
短信发送成功后,将验证码存入redis中,key为该验证码发送到的手机号,还需要设置一个超时时间,比如5分钟:
redisTemplate.opsForValue().set("sms:phone:" + phone, verifyCode, 5, TimeUnit.MINUTES);
设置到redis目的就是为了防止用户短时间内多次调用短信发送接口,分分钟让你破产;
设置了redis,那么每次调用短信发送接口前都需要通过发送用户的手机号判断一下redis中是否有该用户的发送记录,如果有,就不要在调用短信发送逻辑;
这就是大致流程了,当然实际情况肯定比这个复杂,这只是一个大致思路;
大致接口实现如下:
@GetMapping("/{phone}")
public BaseResponse<String> sendMessage(@PathVariable String phone) {
if (StringUtils.isAnyBlank(phone)) {
throw new BusinessException(StatusCode.NULL_ERROR, "手机号为空");
}
// 从redis中查看有没有该手机号的验证码
String verifyCode = (String) redisTemplate.opsForValue().get("sms:phone:" + phone);
if (!StringUtils.isAnyBlank(verifyCode)) {
return ResultUtils.success("验证码已发送");
}
// 如果redis没有该手机号验证码,则获取验证码并发送短信
verifyCode = RandomUtils.getSixBitRandom(); // 获取六位验证码
Map<String, Object> messageMap = new HashMap<>(); // 短信内容{"code":{验证码}}
messageMap.put("code", verifyCode);
Boolean isSend = smsService.sendMessage(messageMap, phone); // 调用短信发送模板
if (isSend) { // 如果发送成功,则将对应手机号验证码存入redis中,设置5分钟内有效
redisTemplate.opsForValue().set("sms:phone:" + phone, verifyCode, 5, TimeUnit.MINUTES);
} else {
throw new BusinessException(StatusCode.SYSTEM_ERROR, "短信发送失败");
}
return ResultUtils.success("短信发送成功");
}
短信发送的学习是一个很简单的调用api接口的案例,阿里云也有很多其他的服务,可以尝试调用它的接口实现更多好玩的功能;