背景
由于阿里云短信服务(测试除外)需要备案+资质,个人测试开发者想要搞到以上两种需要花费点经历时间,所以up找到了第三方服务(阿里云 云市场)赋链接
但是:在第三方发送短信的时候(运营商规定),想要定制模板,也还是需要签名-需要申请,现在签名只能是公司全称,需要您提供营业执照,法人或者代办人身份证号,姓名
以下是up与客服的聊天记录
所以只要没有公司,购买了也还是只能用测试模板,但是至少能收到验证码,请谨慎选择
1.找到入口
2.购买服务
3.查看AppKey
4.测试接口
5.使用
5.1引入依赖
<!-- 短信认证相关依赖start-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.15</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.2.1</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>9.3.7.v20160115</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- 短信认证相关依赖end-->
5.2导入HttpUtils
(这是在请求示例中要求的,在下一步我直接封装了一个,通过code和手机号就可以调用接口的方法)
package com.melody.utils;
/**
* @Author: zengxz
* @Description: HttpUtils(短信服务需要的http类)
* @DateTime: 2025-05-12 17:28
**/
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.junit.platform.commons.util.StringUtils;
public class HttpUtils {
/**
* get
*
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @return
* @throws Exception
*/
public static HttpResponse doGet(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpGet request = new HttpGet(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
return httpClient.execute(request);
}
/**
* post form
*
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @param bodys
* @return
* @throws Exception
*/
public static HttpResponse doPost(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys,
Map<String, String> bodys)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPost request = new HttpPost(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (bodys != null) {
List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>();
for (String key : bodys.keySet()) {
nameValuePairList.add(new BasicNameValuePair(key, bodys.get(key)));
}
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, "utf-8");
formEntity.setContentType("application/x-www-form-urlencoded; charset=UTF-8");
request.setEntity(formEntity);
}
return httpClient.execute(request);
}
/**
* Post String
*
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @param body
* @return
* @throws Exception
*/
public static HttpResponse doPost(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys,
String body)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPost request = new HttpPost(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (StringUtils.isNotBlank(body)) {
request.setEntity(new StringEntity(body, "utf-8"));
}
return httpClient.execute(request);
}
/**
* Post stream
*
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @param body
* @return
* @throws Exception
*/
public static HttpResponse doPost(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys,
byte[] body)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPost request = new HttpPost(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (body != null) {
request.setEntity(new ByteArrayEntity(body));
}
return httpClient.execute(request);
}
/**
* Put String
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @param body
* @return
* @throws Exception
*/
public static HttpResponse doPut(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys,
String body)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPut request = new HttpPut(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (StringUtils.isNotBlank(body)) {
request.setEntity(new StringEntity(body, "utf-8"));
}
return httpClient.execute(request);
}
/**
* Put stream
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @param body
* @return
* @throws Exception
*/
public static HttpResponse doPut(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys,
byte[] body)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPut request = new HttpPut(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (body != null) {
request.setEntity(new ByteArrayEntity(body));
}
return httpClient.execute(request);
}
/**
* Delete
*
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @return
* @throws Exception
*/
public static HttpResponse doDelete(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpDelete request = new HttpDelete(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
return httpClient.execute(request);
}
private static String buildUrl(String host, String path, Map<String, String> querys) throws UnsupportedEncodingException {
StringBuilder sbUrl = new StringBuilder();
sbUrl.append(host);
if (!StringUtils.isBlank(path)) {
sbUrl.append(path);
}
if (null != querys) {
StringBuilder sbQuery = new StringBuilder();
for (Map.Entry<String, String> query : querys.entrySet()) {
if (0 < sbQuery.length()) {
sbQuery.append("&");
}
if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) {
sbQuery.append(query.getValue());
}
if (!StringUtils.isBlank(query.getKey())) {
sbQuery.append(query.getKey());
if (!StringUtils.isBlank(query.getValue())) {
sbQuery.append("=");
sbQuery.append(URLEncoder.encode(query.getValue(), "utf-8"));
}
}
}
if (0 < sbQuery.length()) {
sbUrl.append("?").append(sbQuery);
}
}
return sbUrl.toString();
}
private static HttpClient wrapClient(String host) {
HttpClient httpClient = new DefaultHttpClient();
if (host.startsWith("https://")) {
sslClient(httpClient);
}
return httpClient;
}
private static void sslClient(HttpClient httpClient) {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] xcs, String str) {
}
public void checkServerTrusted(X509Certificate[] xcs, String str) {
}
};
ctx.init(null, new TrustManager[] { tm }, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx);
ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
ClientConnectionManager ccm = httpClient.getConnectionManager();
SchemeRegistry registry = ccm.getSchemeRegistry();
registry.register(new Scheme("https", 443, ssf));
} catch (KeyManagementException ex) {
throw new RuntimeException(ex);
} catch (NoSuchAlgorithmException ex) {
throw new RuntimeException(ex);
}
}
}
5.3调用
package com.melody.melodicmusic;
import com.melody.utils.HttpUtils;
import com.melody.utils.JwtUtil;
import com.melody.utils.ThreadLocalUtil;
import org.apache.http.HttpResponse;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashMap;
import java.util.Map;
@SpringBootTest
class MelodicMusicApplicationTests {
/**
* token生成test
*/
@Test
void tokentest(){
// 如果用查到用户名
// 如果查询到的用户名与输入的密码相同
// 把信息放入token返回
// 创建一个包含用户数据的Map
Map<String, Object> claims = new HashMap<>();
claims.put("userId",1);
claims.put("username", "user1");
// 生成JWT
String token = JwtUtil.genToken(claims);
System.out.println(token);
}
/**
* 解析token测试
*/
@Test
void tokenOKtest(){
//Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGFpbXMiOnsidXNlcklkIjoxLCJ1c2VybmFtZSI6InVzZXIxIn0sImV4cCI6MTc0ODMzMTAxN30.SYFjmlvWmeonCjcE7_CLIn43S4M4W6wKJifS16hW-gA
// (7个字符)
Map<String, Object> tokenMap = JwtUtil.parseToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGFpbXMiOnsidXNlcklkIjoxLCJ1c2VybmFtZSI6InVzZXIxIn0sImV4cCI6MTc0ODMzMTAxN30.SYFjmlvWmeonCjcE7_CLIn43S4M4W6wKJifS16hW-gA");
ThreadLocalUtil.set(tokenMap);
System.out.println(ThreadLocalUtil.get().toString());
// 从 userInfo 中获取 userid
Object userId = tokenMap.get("userId");
System.out.println(userId.toString());
}
/**
* 短信验证测试
*/
@Test
void noteVerificationTest(){
sendMsg("6666","18760372913");
}
/**
* 请求方法
* @param code
* @param phone
*/
public void sendMsg(String code, String phone) {
//调用的URL
String host = "https://dfsns.market.alicloudapi.com";
String path = "/data/send_sms";
//请求方式
String method = "POST";
//自己的appcode----------------------------------Start----------------------------------------------
String appcode = "86b7a3fbc3eb4fee87a3eee40b95ba1a";
//自己的appcode----------------------------------end----------------------------------------------
Map<String, String> headers = new HashMap<>();
//最后在header中的格式(中间是英文空格)为Authorization:APPCODE 自己的appcode
headers.put("Authorization", "APPCODE " + appcode);
//根据API的要求,定义相对应的Content-Type
headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
Map<String, String> querys = new HashMap<>();
Map<String, String> bodys = new HashMap<>();
bodys.put("content", "code:" + code);
bodys.put("template_id", "CST_ptdie100"); //注意,CST_ptdie100该模板ID仅为调试使用,调试结果为"status": "OK" ,即表示接口调用成功,然后联系客服报备自己的专属签名模板ID,以保证短信稳定下发
bodys.put("phone_number", phone);
try {
HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys);
System.out.println("--------test-------------这是测试请求的请求返回值"+response.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试完,是真的能收到了
之后自行保存sendMsg()函数,建议封装个工具类(后面附带)可以重复使用
6.springboot调用
原理:随机生成4为数字验证码,根据手机号设置redis 5分钟过期,登录和验证的时候根据手机号查询redis,查询验证码是否正确
6.1发送验证码
package com.melody.controller;
/**
* @Author: zengxz
* @Description: 请求验证码controller类
* @DateTime: 2025-05-13 17:04
**/
import com.melody.common.component.SmsComponent;
import com.melody.entry.Result;
import com.melody.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Random;
@RestController
@RequestMapping("/authCode")
public class VerificationCodeController {
@Autowired
private RedisUtil redisUtil; // 注入 Redis 工具类
@Autowired
private SmsComponent smsComponent;
/**
* 请求验证码接口
* @param phone 用户ID(可以是手机号、邮箱或其他唯一标识)
* @return 返回生成的验证码(实际项目中应发送到用户手机或邮箱,这里直接返回用于测试)
*/
@GetMapping
public Result<String> requestVerificationCode(@RequestParam String phone) {
// 1.生成4位数字验证码
String verificationCode = generateVerificationCode();
// 2.发送验证码
smsComponent.sendMsg(verificationCode, phone);
// 3.将验证码存储到 Redis,设置过期时间为5分钟
redisUtil.setVerificationCode("phone:code:" + phone, verificationCode, 60*5);
// 4.返回验证码(实际项目中可以返回一个成功消息,验证码通过其他方式发送给用户)
System.out.println("验证码已发送,验证码为:" + verificationCode);
return Result.success("验证码已发送");
}
/**
* 生成4位数字验证码
* @return 验证码字符串
*/
private String generateVerificationCode() {
Random random = new Random();
return String.format("%04d", random.nextInt(10000)); // 生成0000到9999的随机数
}
}
6.2引入redis依赖
<!--redis坐标-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
6.3封装redis工具
package com.melody.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* @Author: zengxz
* @Description: redis验证码工具
* @DateTime: 2025-05-13 16:52
**/
@Component
public class RedisUtil {
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 设置验证码到 Redis,并设置过期时间
* @param key Redis 键
* @param value 验证码值
* @param timeout 过期时间(秒)
*/
public void setVerificationCode(String key, String value, long timeout) {
stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
}
/**
* 从 Redis 获取验证码
* @param key Redis 键
* @return 验证码值
*/
public String getVerificationCode(String key) {
return stringRedisTemplate.opsForValue().get(key);
}
/**
* 删除验证码
* @param key Redis 键
*/
public void deleteVerificationCode(String key) {
stringRedisTemplate.delete(key);
}
}
6.4实现验证码注册和验证码登录
dto等参数请根据自己需求修改
controller
package com.melody.controller;
import com.melody.entry.Result;
import com.melody.entry.UserInfo;
import com.melody.model.dto.UserSaveDto;
import com.melody.model.dto.UserRegisterDto;
import com.melody.service.UserInfoService;
import com.melody.utils.ThreadLocalUtil;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
/**
* @Author: zengxz
* @Description: 用户信息
* @DateTime: 2025-05-08 21:58
**/
@RestController
@RequestMapping("/user")
public class UserInfoController {
@Resource
private UserInfoService userInfoService;
/**
* 用户注册
* @param userRegisterDto
* @return
*/
@PostMapping("/register")
public Result<String> userRegister(@RequestBody UserRegisterDto userRegisterDto){
return userInfoService.userRegister(userRegisterDto);
}
/**
* 验证码登录
*/
@PostMapping("/codeLogin")
public Result<String> codeLogin(@RequestBody UserSaveDto userSaveDto) {
return userInfoService.codeLogin(userSaveDto);
}
}
service就免了,我们直接看serviceImpl
package com.melody.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.melody.common.component.SmsComponent;
import com.melody.entry.Result;
import com.melody.entry.UserInfo;
import com.melody.model.dto.UserRegisterDto;
import com.melody.model.dto.UserSaveDto;
import com.melody.model.vo.UserInfoVo;
import com.melody.service.UserInfoService;
import com.melody.mapper.UserInfoMapper;
import com.melody.utils.JwtUtil;
import com.melody.utils.RedisUtil;
import com.melody.utils.ThreadLocalUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
/**
* @author zxz
* @description 针对表【user_info(用户信息表)】的数据库操作Service实现
* @createDate 2025-05-09 10:31:18
*/
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo>
implements UserInfoService{
@Autowired
private RedisUtil redisUtil;
/**
* 用户注册
* @param userRegisterDto
* @return
*/
@Override
public Result<String> userRegister(UserRegisterDto userRegisterDto) {
// 1.判断信息是否完全
if (userRegisterDto == null || userRegisterDto.getUserName() == null || userRegisterDto.getPhone() == null || userRegisterDto.getPassword() == null){
return Result.error("请填写完整信息");
}
// 2.redis获取验证码
String redisCode = redisUtil.getVerificationCode( "phone:code:"+userRegisterDto.getPhone());
if (redisCode == null){
return Result.error("验证码过期");
}
// 3.判断验证码是否正确
if (!redisCode.equals(userRegisterDto.getVerificationCode())){
return Result.error("验证码错误");
}
// 4.插入数据库语句
UserInfo userInfo = new UserInfo();
userInfo.setUserName(userRegisterDto.getUserName());
userInfo.setPhone(userRegisterDto.getPhone());
userInfo.setPassword(userRegisterDto.getPassword());
userInfo.setType(1);
if (save(userInfo)){
// 5.返回token
Map<String, Object> claims = new HashMap<>();
claims.put("userId",userInfo.getUserId() );
claims.put("username", userInfo.getUserName());
// 生成JWT
String token = JwtUtil.genToken(claims);
return Result.success(token);
}
return Result.error("注册失败");
}
/**
* 验证码登录
* @param userSaveDto
* @return
*/
@Override
public Result<String> codeLogin(UserSaveDto userSaveDto) {
if (userSaveDto == null || userSaveDto.getPhone() == null || userSaveDto.getCode() == null){
return Result.error("请填写完整信息");
}
String redisCode = redisUtil.getVerificationCode( "phone:code:"+userSaveDto.getPhone());
if (redisCode == null){
return Result.error("验证码过期");
}
UserInfo one = null;
if (redisCode.equals(userSaveDto.getCode())){
LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserInfo::getPhone,userSaveDto.getPhone());
one = getOne(queryWrapper);
// 创建一个包含用户数据的Map
Map<String, Object> claims = new HashMap<>();
claims.put("userId",one.getUserId() );
claims.put("username", one.getUserName());
// 生成JWT
String token = JwtUtil.genToken(claims);
return Result.success(token);
}
return Result.error("验证码错误");
}
}