進入目标:
(1)掌握Spring Boot 架構的搭建方法
(2)能夠使用阿裡大于發送短信
(3)運用 Spring Boot、阿裡大于和ActiveMQ 開發短信微服務
(4)完成品優購使用者注冊功能(短信驗證碼認證)
目錄
1、短信微服務
1.1 需求分析
1.2 搭建工程
2、使用者注冊
2.1 需求分析
2.2 工程搭建(參考其他服務層工程和WEB層)
2.3 基本注冊功能實作
2.4 使用者注冊-發送短信驗證碼
2.5 使用者注冊-校驗短信驗證碼
1、短信微服務
1.1 需求分析
建構一個通用的短信發送服務(獨立于品優購的單獨工程),接收activeMQ的消息(MAP類型) 消息包括手機号(mobile)、短信模闆号(template_code)、簽名(sign_name)、參數字元串(param)
1.2 搭建工程
(1)建立spring boot工程,并引入相關依賴
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xushuai</groupId>
<artifactId>sms-service</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>3.2.5</version>
</dependency>
</dependencies>
</project>
(2)建立引導類
package com.xushuai.sms;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 短信服務 引導類
* Author xushuai
* Description
*/
@SpringBootApplication
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class, args);
}
}
(3)建立配置檔案(application.properties),填入accessKey的相關資訊
server.port=9003
spring.activemq.broker-url=tcp://192.168.25.170:61616
accessKeyId=accessKeyId
accessKeySecret=accessKeySecret
(4)複制阿裡大于的API Demo工程中的SmsDemo,然後進行修改
package com.xushuai.sms.util;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.dysmsapi.transform.v20170525.SendSmsResponseUnmarshaller;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.FormatType;
import com.aliyuncs.http.HttpResponse;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
/**
* 短信API産品的DEMO程式,工程中包含了一個SmsDemo類,直接通過
* 執行main函數即可體驗短信産品API功能(隻需要将AK替換成開通了雲通信-短信産品功能的AK即可)
* 工程依賴了2個jar包(存放在工程的libs目錄下)
* 1:aliyun-java-sdk-core.jar
* 2:aliyun-java-sdk-dysmsapi.jar
*
* 備注:Demo工程編碼采用UTF-8
* 國際短信發送請勿參照此DEMO
*/
@Component
public class SmsUtil {
//産品名稱:雲通信短信API産品,開發者無需替換
static final String product = "Dysmsapi";
//産品域名,開發者無需替換
static final String domain = "dysmsapi.aliyuncs.com";
// TODO 此處需要替換成開發者自己的AK(在阿裡雲通路控制台尋找)
@Autowired
private Environment env;
public SendSmsResponse sendSms(String mobile, String templateCode, String signName, String param) throws ClientException {
// 擷取AK
String accessKeyId = env.getProperty("accessKeyId");
String accessKeySecret = env.getProperty("accessKeySecret");
//可自助調整逾時時間
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000");
//初始化acsClient,暫不支援region化
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
IAcsClient acsClient = new DefaultAcsClient(profile);
//組裝請求對象-具體描述見控制台-文檔部分内容
SendSmsRequest request = new SendSmsRequest();
//必填:待發送手機号
request.setPhoneNumbers(mobile);
//必填:短信簽名-可在短信控制台中找到
request.setSignName(signName);
//必填:短信模闆-可在短信控制台中找到
request.setTemplateCode(templateCode);
//可選:模闆中的變量替換JSON串,如模闆内容為"親愛的${name},您的驗證碼為${code}"時,此處的值為
request.setTemplateParam(param);
//選填-上行短信擴充碼(無特殊需求使用者請忽略此字段)
//request.setSmsUpExtendCode("90997");
//可選:outId為提供給業務方擴充字段,最終在短信回執消息中将此值帶回給調用者
request.setOutId("yourOutId");
//hint 此處可能會抛出異常,注意catch
SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
return sendSmsResponse;
}
public QuerySendDetailsResponse querySendDetails(String mobile, String bizId) throws ClientException {
// 擷取AK
String accessKeyId = env.getProperty("accessKeyId");
String accessKeySecret = env.getProperty("accessKeySecret");
//可自助調整逾時時間
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000");
//初始化acsClient,暫不支援region化
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
IAcsClient acsClient = new DefaultAcsClient(profile);
//組裝請求對象
QuerySendDetailsRequest request = new QuerySendDetailsRequest();
//必填-号碼
request.setPhoneNumber(mobile);
//可選-流水号
request.setBizId(bizId);
//必填-發送日期 支援30天内記錄查詢,格式yyyyMMdd
SimpleDateFormat ft = new SimpleDateFormat("yyyyMMdd");
request.setSendDate(ft.format(new Date()));
//必填-頁大小
request.setPageSize(10L);
//必填-目前頁碼從1開始計數
request.setCurrentPage(1L);
//hint 此處可能會抛出異常,注意catch
QuerySendDetailsResponse querySendDetailsResponse = acsClient.getAcsResponse(request);
return querySendDetailsResponse;
}
}
(5)編寫 activeMQ監聽器類
package com.xushuai.sms.listener;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.xushuai.sms.util.SmsUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 發送短信監聽器
* Author xushuai
* Description
*/
@Component
public class SmsListener {
@Autowired
private SmsUtil smsUtil;
@JmsListener(destination = "sms")
public void sendSms(Map<String, String> map) {
try {
SendSmsResponse response = smsUtil.sendSms(map.get("mobile"),
map.get("templateCode"),
map.get("SignName"),
map.get("param"));
System.out.println(response.getCode());
System.out.println(response.getMessage());
} catch (ClientException e) {
e.printStackTrace();
}
}
}
2、使用者注冊
2.1 需求分析
完成使用者注冊功能
2.2 工程搭建(參考其他服務層工程和WEB層)
(1)分别為:pinyougou-user-interface、pinyougou-user-service和pinyougou-user-web
2.3 基本注冊功能實作
(1)後端:服務層實作,修改UserServicceImpl中的add方法的邏輯
/**
* 增加
*/
@Override
public void add(TbUser user) {
// 補全資料
// 建立和更新時間
user.setCreated(new Date());
user.setUpdated(new Date());
// 注冊來源
user.setSourceType(TbUser.SOURCE_TYPE_PC);
// 對密碼進行加密
String md5Hex = DigestUtils.md5Hex(user.getPassword());
user.setPassword(md5Hex);
userMapper.insert(user);
}
(2)前端:引入JS檔案和基礎指令
(3)前端:在userController.js中新增方法
// 注冊方法
$scope.reg = function () {
if ($scope.entity == null || $scope.entity.username == null || $scope.entity.username.trim() == '' ||
$scope.entity.password == null || $scope.entity.password.trim() == '' || $scope.entity.phone == null ||
$scope.entity.phone.trim() == '') {
alert("請完成填寫您的資料");
return ;
}
if ($scope.entity.password != $scope.password) {
alert("兩次輸入的密碼不一緻!");
return ;
}
// alert(JSON.stringify($scope.entity));
userService.add($scope.entity).success(
function (rtn) {
alert(rtn.message);
}
);
}
(4)前端:頁面綁定變量和單擊事件
2.4 使用者注冊-發送短信驗證碼
(1)實作思路
點選頁面上的”擷取短信驗證碼”連接配接,向後端傳遞手機号。後端随機生成6位數字作為短信驗證碼,将其儲存在redis中(手機号作為KEY),并發送到短信網關。
使用者注冊時,後端根據手機号查詢redis中的驗證碼與使用者填寫的驗證碼是否相同,如果不同則提示使用者不能注冊。
(2)後端:服務層接口,在UserService中新增方法
/**
* 發送短信驗證碼
*
* @param phone 接收驗證碼的手機号
*/
void sendSmsCode(String phone);
(3)後端:服務層實作,在UserServiceImpl中實作
@Override
public void sendSmsCode(final String phone) {
// 生成6位數字驗證碼
final String code = String.valueOf((long) (Math.random() * 1000000));
// 将驗證碼放入redis緩存,以手機号為key
redisTemplate.boundHashOps("smsCode").put(phone, code);
// 發送短信消息
System.out.println("驗證碼:" + code);
jmsTemplate.send(smsDestination, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
MapMessage message = session.createMapMessage();
message.setString("mobile", phone);
message.setString("templateCode", templateCode);
message.setString("signName",signName);
// 設定替換參數
Map<String, String> map = new HashMap<>();
map.put("code", code);
message.setString("param", JSON.toJSONString(map));
return message;
}
});
}
注意:需要配置常量到properties配置檔案
(4)後端:控制層,在UserController中新增方法
/**
* 發送短信驗證碼
*/
@RequestMapping("/sendCode")
public Result sendCode(String phone) {
try {
// 校驗phone是否合法
if (!PhoneFormatCheckUtils.isPhoneLegal(phone)) {
return Result.error("手機号格式不正确");
}
userService.sendSmsCode(phone);
return Result.success("發送成功");
}catch (Exception e){
e.printStackTrace();
return Result.error("驗證碼發送異常");
}
}
(5)前端:userService.js中新增方法
// 發送短信驗證碼
this.sendCode = function (phone) {
return $http.get('../user/sendCode.do?phone=' + phone);
}
(6)前端:userController.js新增方法
// 發送短信驗證碼
$scope.sendCode = function () {
if ($scope.entity.phone == null || $scope.entity.phone.trim() == "") {
alert("請正确填寫手機号!");
return ;
}
userService.sendCode($scope.entity.phone).success(
function (rtn) {
alert(rtn.message);
}
);
}
(7)頁面綁定單擊事件
2.5 使用者注冊-校驗短信驗證碼
(1)後端:服務層接口,UserService中新增方法
/**
* 判斷使用者輸入的驗證碼是否正确
*
* @param phone 接收短信的手機号
* @param code 使用者輸入的驗證碼
* @return boolean
*/
boolean checkSmsCode(String phone, String code);
(2)後端:服務層實作,UserServiceImpl實作
@Override
public boolean checkSmsCode(String phone, String code) {
// 通過手機号,從redis中擷取驗證碼
String smsCode = (String) redisTemplate.boundHashOps("smsCode").get(phone);
if (smsCode == null || smsCode.trim().equals("")) {
return false;
}
// 判斷驗證碼是否正确
return code.equals(smsCode);
}
(3)後端:控制層,修改UserController中的add方法
/**
* 增加
*
* @param user 使用者資料
* @param smsCode 驗證碼
* @return
*/
@RequestMapping("/add")
public Result add(@RequestBody TbUser user, String smsCode) {
try {
// 校驗短信驗證碼
if (!userService.checkSmsCode(user.getPhone(), smsCode)) {
return Result.error("驗證碼錯誤!");
}
userService.add(user);
return new Result(true, "注冊成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false, "注冊失敗");
}
}
(4)前端:修改userService.js中的add方法
//增加
this.add=function(entity,smsCode){
return $http.post('../user/add.do?smsCode=' + smsCode,entity );
}
(5)前端:修改userController.js中的reg方法
(6)前端:頁面綁定變量