一、准备工作

1、官方文档

阿里云短信 API 调试:API 调试

阿里云短信 API:API 文档

2、配置依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--lombok用来简化实体类:需要安装lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

<!--阿里云短信-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.5.16</version>
</dependency>

<!-- 配置文件处理器 -->
<!--让自定义的配置在application.yaml进行自动提示-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

3、自定义配置的提示信息

设置搜索processor

重新编译模块,之后就可以在application.yml文件中有自定义配置的提示信息

4、配置文件

在 application.yml 配置文件中进行配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server:
port: 8120 # 服务端口

spring:
profiles:
active: dev # 环境设置
application:
name: service-sms # 服务名

#阿里云短信
aliyun:
sms:
region-id:
key-id:
key-secret:
template-code:
sign-name:

二、工具类

1、从配置文件读取常量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Setter
@Getter //idea2020.2.3版配置文件自动提示需要这个
@Component
@ConfigurationProperties(prefix = "aliyun.sms")
public class SmsProperties implements InitializingBean {

private String regionId;
private String keyId;
private String keySecret;
private String templateCode;
private String signName;

public static String REGION_Id;
public static String KEY_ID;
public static String KEY_SECRET;
public static String TEMPLATE_CODE;
public static String SIGN_NAME;

/**
* 当私有成员被赋值后,此方法自动被调用,从而初始化常量
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
REGION_Id = regionId;
KEY_ID = keyId;
KEY_SECRET = keySecret;
TEMPLATE_CODE = templateCode;
SIGN_NAME = signName;
}
}

2、生成验证码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
* 生成四位和六位的随机数字
*/
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个数据
*
* @param list
* @param n
* @return
*/
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]));
System.out.print(list.get((int) robjs[i]) + "\t");
}
System.out.print("\n");
return r;
}
}

3、手机号码正则匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/**
* 使用正则表达式进行表单验证
*/
public class RegexValidateUtils {

static boolean flag = false;
static String regex = "";

public static boolean check(String str, String regex) {
try {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
flag = matcher.matches();
} catch (Exception e) {
flag = false;
}
return flag;
}

/**
* 验证邮箱
*
* @param email
* @return
*/
public static boolean checkEmail(String email) {
String regex = "^\\w+[-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$ ";
return check(email, regex);
}

/**
* 验证手机号码
* <p>
* 移动号码段:139、138、137、136、135、134、150、151、152、157、158、159、182、183、184、187、188、147
* 联通号码段:130、131、132、136、185、186、145
* 电信号码段:133、153、180、189
*
* @param cellphone
* @return
*/
public static boolean checkCellphone(String cellphone) {
String regex = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,4-9]))\\d{8}$";
return check(cellphone, regex);
}

/**
* 验证固话号码
*
* @param telephone
* @return
*/
public static boolean checkTelephone(String telephone) {
String regex = "^(0\\d{2}-\\d{8}(-\\d{1,4})?)|(0\\d{3}-\\d{7,8}(-\\d{1,4})?)$";
return check(telephone, regex);
}

/**
* 验证传真号码
*
* @param fax
* @return
*/
public static boolean checkFax(String fax) {
String regex = "^(0\\d{2}-\\d{8}(-\\d{1,4})?)|(0\\d{3}-\\d{7,8}(-\\d{1,4})?)$";
return check(fax, regex);
}

/**
* 验证QQ号码
*
* @param QQ
* @return
*/
public static boolean checkQQ(String QQ) {
String regex = "^[1-9][0-9]{4,} $";
return check(QQ, regex);
}
}

三、实现发送短信

1、自定义断言

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@Slf4j
public abstract class Assert {


/**
* 断言表达式为真
* 如果不为真,则抛出异常
*
* @param expression
* @param msg
*/
public static void isTrue(boolean expression, String msg) {
if (!expression) {
log.info("fail...............");
}
}


/**
* 断言两个对象不相等
* 如果相等,则抛出异常
*
* @param m1
* @param m2
* @param msg
*/
public static void notEquals(Object m1, Object m2, String msg) {
if (m1.equals(m2)) {
log.info("equals...............");
}
}


/**
* 断言两个对象相等
* 如果不相等,则抛出异常
*
* @param m1
* @param m2
* @param msg
*/
public static void equals(Object m1, Object m2, String msg) {
if (!m1.equals(m2)) {
log.info("not equals...............");
}
}

/**
* 断言参数不为空
* 如果为空,则抛出异常
*
* @param s
* @param msg
*/
public static void notEmpty(String s, String msg) {
if (StringUtils.isEmpty(s)) {
log.info("is empty...............");
}
}
}

2、接口

创建 SmsService

1
2
3
4
5
6
7
8
9
public interface SmsService {
/**
* 发送短信
* @param mobile 手机号
* @param templateCode 短信模板CODE
* @param param 验证码
*/
void send(String mobile, String templateCode, Map<String,Object> param);
}

3、实现类

创建 SmsServiceImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@Service
@Slf4j
public class SmsServiceImpl implements SmsService {

@Override
public void send(String mobile, String templateCode, Map<String, Object> param) {

//创建远程连接客户端对象
DefaultProfile profile = DefaultProfile.getProfile(
SmsProperties.REGION_Id,
SmsProperties.KEY_ID,
SmsProperties.KEY_SECRET);
IAcsClient client = new DefaultAcsClient(profile);

//创建远程连接的请求参数
CommonRequest request = new CommonRequest();
request.setSysMethod(MethodType.POST);
request.setSysDomain("dysmsapi.aliyuncs.com");
request.setSysVersion("2017-05-25");
request.setSysAction("SendSms");
request.putQueryParameter("RegionId", SmsProperties.REGION_Id);
request.putQueryParameter("PhoneNumbers", mobile);
request.putQueryParameter("SignName", SmsProperties.SIGN_NAME);
request.putQueryParameter("TemplateCode", templateCode);

Gson gson = new Gson();
String json = gson.toJson(param);
request.putQueryParameter("TemplateParam", json);

try {
// 使用客户端对象携带请求对象发送请求,并得到响应结果
CommonResponse response = client.getCommonResponse(request);

// 通信失败的处理
boolean success = response.getHttpResponse().isSuccess();
Assert.isTrue(success, "阿里云短信服务响应失败");

// 获取响应结果
String data = response.getData();
HashMap<String, String> resultMap = gson.fromJson(data, HashMap.class);
String code = resultMap.get("Code");
String message = resultMap.get("Message");
log.info("code" + code + ",message" + message);

// 业务失败的处理
//ALIYUN_SMS_LIMIT_CONTROL_ERROR(-502, "短信发送过于频繁"),//业务限流
Assert.notEquals("isv.BUSINESS_LIMIT_CONTROL", code, "短信发送过于频繁");
//ALIYUN_SMS_ERROR(-503, "短信发送失败"),//其他失败
Assert.equals("OK", code, "短信发送失败");

} catch (ServerException e) {
log.error("阿里云短信发送SDK调用失败:" + e.getErrCode() + "," + e.getErrMsg());
} catch (ClientException e) {
log.error("阿里云短信发送SDK调用失败:" + e.getErrCode() + "," + e.getErrMsg());
}
}
}

3、控制层

创建类 ApiSmsController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@RestController
@RequestMapping("/api/sms")
@CrossOrigin
@Slf4j
public class ApiSmsController {

@Resource
private SmsService smsService;

@GetMapping("/send/{mobile}")
public String send(@PathVariable String mobile) {

//MOBILE_NULL_ERROR(-202, "手机号不能为空"),
Assert.notEmpty(mobile, "手机号不能为空");
//MOBILE_ERROR(-203, "手机号不正确"),
Assert.isTrue(RegexValidateUtils.checkCellphone(mobile), "手机号不正确");

//生成验证码
String code = RandomUtils.getFourBitRandom();
//组装短信模板参数
Map<String, Object> param = new HashMap<>();
param.put("code", code);
//发送短信
smsService.send(mobile, SmsProperties.TEMPLATE_CODE, param);

return "短信发送成功";
}
}

4、测试

访问端口:http://localhost:8120/api/sms/send/手机号

5、报错

期间出现了:codeisv.SMS_SIGNATURE_ILLEGAL,message 签名不合法(不存在或被拉黑)

重点检查 application.yml 配置文件,查看阿里云的配置信息,还有中文的编码格式