架构流程
# 短信的架构
# 手机验证码
## 1. 申请
>阿里云个人是无法申请手机验证码的, 因此, 我们采取下面的方式弄一个测试手机, 用测试收集收验证码
> 加入短信权限



## 2. 环境构建

> 点击SDK信息获取依赖版本号
## 逆天大坑
### 发送请求, 总是显示accessKey不存在
> 因为仔细看官方给的SDK, 里面有一段注解, 中文意思是在环境变量里面获取, 对应着getEnv()这个方法, 设置环境变量没有用的话, 把他删了, 直接注入
### 方法异步执行, 先于controller执行
> 这是因为有@ConfigurationProperties注解, 导致异步执行, 即声明的这个组件会执行里面所有的方法, 不管是否@Bean, 因此异步
## 代码
```java
package com.junjie.bitmall.thirdpart.component;
import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.sdk.service.dysmsapi20170525.AsyncClient;
import com.aliyun.sdk.service.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.sdk.service.dysmsapi20170525.models.SendSmsResponse;
import com.google.gson.Gson;
import darabonba.core.client.ClientOverrideConfiguration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* @className: com.junjie.bitmall.thirdpart.component.SMSComponent
* @description:
* @author: 江骏杰
* @create: 2023-08-15 17:56
*/
@Component
public class SMSComponent {
@Value("${spring.cloud.alicloud.access-key}")
private String accessId;
@Value("${spring.cloud.alicloud.secret-key}")
private String accessKey;
public void sendPhoneCode(String phone, String code) throws ExecutionException, InterruptedException {
// HttpClient Configuration
/*HttpClient httpClient = new ApacheAsyncHttpClientBuilder()
.connectionTimeout(Duration.ofSeconds(10)) // Set the connection timeout time, the default is 10 seconds
.responseTimeout(Duration.ofSeconds(10)) // Set the response timeout time, the default is 20 seconds
.maxConnections(128) // Set the connection pool size
.maxIdleTimeOut(Duration.ofSeconds(50)) // Set the connection pool timeout, the default is 30 seconds
// Configure the proxy
.proxy(new ProxyOptions(ProxyOptions.Type.HTTP, new InetSocketAddress("<your-proxy-hostname>", 9001))
.setCredentials("<your-proxy-username>", "<your-proxy-password>"))
// If it is an https connection, you need to configure the certificate, or ignore the certificate(.ignoreSSL(true))
.x509TrustManagers(new X509TrustManager[]{})
.keyManagers(new KeyManager[]{})
.ignoreSSL(false)
.build();*/
// Configure Credentials authentication information, including ak, secret, token
StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()
// Please ensure that the environment variables ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET are set.
.accessKeyId(accessId) // 这里有大坑, 默认从环境变量里面获取, 实测无效, 因此改成这样
.accessKeySecret(accessKey)
//.securityToken(System.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")) // use STS token
.build());
// Configure the Client
AsyncClient client = AsyncClient.builder()
.region("cn-hangzhou") // Region ID
//.httpClient(httpClient) // Use the configured HttpClient, otherwise use the default HttpClient (Apache HttpClient)
.credentialsProvider(provider)
//.serviceConfiguration(Configuration.create()) // Service-level configuration
// Client-level configuration rewrite, can set Endpoint, Http request parameters, etc.
.overrideConfiguration(
ClientOverrideConfiguration.create()
// Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi
.setEndpointOverride("dysmsapi.aliyuncs.com")
//.setConnectTimeout(Duration.ofSeconds(30))
)
.build();
// Parameter settings for API request
SendSmsRequest sendSmsRequest = SendSmsRequest.builder()
.signName("阿里云短信测试")
.templateCode("SMS_154950909")
.phoneNumbers("15570790850")
.templateParam("{\"code\":\""+code+"\"}") // 验证码
// Request-level configuration rewrite, can set Http request parameters, etc.
// .requestConfiguration(RequestConfiguration.create().setHttpHeaders(new HttpHeaders()))
.build();
// Asynchronously get the return value of the API request
CompletableFuture<SendSmsResponse> response = client.sendSms(sendSmsRequest);
// Synchronously get the return value of the API request
SendSmsResponse resp = null;
resp = response.get();
System.out.println(new Gson().toJson(resp));
// Asynchronous processing of return values
// response.thenAccept(resp ->
// System.out.println(new Gson().toJson(resp))
// ).exceptionally(throwable -> { // Handling exceptions
// System.out.println(throwable.getMessage());
// return null;
// });
// Finally, close the client
client.close();
}
}
```
# 邮箱验证码
## 架构
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
```
```yml
mail:
host: smtp.126.com
username: jiangjunjie_db@126.com
password: XWFXSGJZJDVXONSG
port: 465
#XWFXSGJZJDVXONSG
protocol: smtp
default-encoding: UTF-8
properties:
mail.smtp.auth: true
mail.smtp.starttls.enable: true
mail.smtp.starttls.required: true
mail.smtp.socketFactory.port: 465
mail.smtp.socketFactory.class: javax.net.ssl.SSLSocketFactory
mail.smtp.socketFactory.fallback: false
```
```java
/**
*
* @param to 收件人邮箱
* @param emailTitle 邮件标题
* @param code 验证码
*/
public void sendEmailCode(String to, String emailTitle, String code) {
// 封装视图, 并渲染视图, 最终获取对应的字符串
Context context = new Context();
context.setVariable("verifyCode", Arrays.asList(code.split("")));
String emailContent = templateEngine.process("mail", context);
// 自定义消息
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = null;
try {
helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(emailTitle);
helper.setText(emailContent, true);
} catch (MessagingException e) {
e.printStackTrace();
}
mailSender.send(message);
}
```
```html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>邮箱验证码</title>
<style>
table {
width: 700px;
margin: 0 auto;
}
#top {
width: 700px;
border-bottom: 1px solid #ccc;
margin: 0 auto 30px;
}
#top table {
font: 12px Tahoma, Arial, 宋体;
height: 40px;
}
#content {
width: 680px;
padding: 0 10px;
margin: 0 auto;
}
#content_top {
line-height: 1.5;
font-size: 14px;
margin-bottom: 25px;
color: #4d4d4d;
}
#content_top strong {
display: block;
margin-bottom: 15px;
}
#content_top strong span {
color: #f60;
font-size: 16px;
}
#verificationCode {
color: #f60;
font-size: 24px;
}
#content_bottom {
margin-bottom: 30px;
}
#content_bottom small {
display: block;
margin-bottom: 20px;
font-size: 12px;
color: #747474;
}
#bottom {
width: 700px;
margin: 0 auto;
}
#bottom div {
padding: 10px 10px 0;
border-top: 1px solid #ccc;
color: #747474;
margin-bottom: 20px;
line-height: 1.3em;
font-size: 12px;
}
#content_top strong span {
font-size: 18px;
color: #FE4F70;
}
#sign {
text-align: right;
font-size: 18px;
color: #FE4F70;
font-weight: bold;
}
#verificationCode {
height: 100px;
width: 680px;
text-align: center;
margin: 30px 0;
}
#verificationCode div {
height: 100px;
width: 680px;
}
.button {
color: #FE4F70;
margin-left: 10px;
height: 80px;
width: 80px;
resize: none;
font-size: 42px;
border: none;
outline: none;
padding: 10px 15px;
background: #ededed;
text-align: center;
border-radius: 17px;
box-shadow: 6px 6px 12px #cccccc,
-6px -6px 12px #ffffff;
}
.button:hover {
box-shadow: inset 6px 6px 4px #d1d1d1,
inset -6px -6px 4px #ffffff;
}
</style>
</head>
<body>
<table>
<tbody>
<tr>
<td>
<div id="top">
<table>
<tbody><tr><td></td></tr></tbody>
</table>
</div>
<div id="content">
<div id="content_top">
<strong>尊敬的用户:您好!</strong>
<strong>
您正在进行<span>注册账号</span>操作,请在验证码中输入以下验证码完成操作:
</strong>
<div id="verificationCode">
<button class="button" th:each="a:${verifyCode}">[[${a}]]</button>
</div>
</div>
<div id="content_bottom">
<small>
注意:此操作可能会修改您的密码、登录邮箱或绑定手机。如非本人操作,请及时登录并修改密码以保证账号安全
<br>(工作人员不会向你索取此验证码,请勿泄漏!)
</small>
</div>
</div>
<div id="bottom">
<div>
<p>此为系统邮件,请勿回复<br>
请保管好您的邮箱,避免账号被他人盗用
</p>
<p id="sign">——JUNJIE_SEND</p>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
```