1、引入依赖
<!--短信jar包-->
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
<!--SpringBoot Boot Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 提供Redis连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
2、验证码过期时间可以使用reids实现,配置redis
spring:
# redis配置
redis:
# Redis数据库索引(默认为0)
database: 0
# Redis服务器地址
host: localhost
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password: 123456
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
3、编写reids工具类
package com.cloud.web.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* spring redis 工具类
*
* @author hdb
**/
@Component
public class RedisUtil
{
@Autowired
public RedisTemplate redisTemplate;
public String doString(final byte[] bytes){
return (String) redisTemplate
.getValueSerializer()
.deserialize(bytes);
}
public void release(final String key,final Object value){
redisTemplate.convertAndSend(key,value);
}
/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
*/
public <T> void setCacheObject(final String key, final T value)
{
redisTemplate.opsForValue().set(key, value);
}
public void expire(final String key, final long timeout)
{
expire(key,null,timeout,TimeUnit.DAYS);
}
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
*/
public <T> void expire(final String key,final T value, final long timeout)
{
expire(key,value,timeout,TimeUnit.DAYS);
}
/**
* 设置有效时间
*
* @param key Redis键
* @param value 值
* @param timeout 超时时间
* @param unit 时间单位
*/
public <T> void expire(final String key,final T value, final long timeout, final TimeUnit unit)
{
redisTemplate.opsForValue().set(key,value,timeout,unit);
}
/**
* 判断 key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public Boolean hasKey(String key)
{
return redisTemplate.hasKey(key);
}
/**
* 获得缓存的基本对象。
*
* @param key 缓存键值
* @return 缓存键值对应的数据
*/
public <T> T getCacheObject(final String key)
{
ValueOperations<String, T> operation = redisTemplate.opsForValue();
return operation.get(key);
}
/**
* 删除单个对象
*
* @param key
*/
public boolean deleteObject(final String key)
{
return redisTemplate.delete(key);
}
/**
* 删除集合对象
*
* @param collection 多个对象
* @return
*/
public long deleteObject(final Collection collection)
{
return redisTemplate.delete(collection);
}
/**
* 缓存List数据
*
* @param key 缓存的键值
* @param dataList 待缓存的List数据
* @return 缓存的对象
*/
public <T> long setCacheList(final String key, final List<T> dataList)
{
Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
return count == null ? 0 : count;
}
/**
* 获得缓存的list对象
*
* @param key 缓存的键值
* @return 缓存键值对应的数据
*/
public <T> List<T> getCacheList(final String key)
{
return redisTemplate.opsForList().range(key, 0, -1);
}
/**
* 缓存Set
*
* @param key 缓存键值
* @param dataSet 缓存的数据
* @return 缓存数据的对象
*/
public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
{
BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
Iterator<T> it = dataSet.iterator();
while (it.hasNext())
{
setOperation.add(it.next());
}
return setOperation;
}
/**
* 获得缓存的set
*
* @param key
* @return
*/
public <T> Set<T> getCacheSet(final String key)
{
return redisTemplate.opsForSet().members(key);
}
/**
* 缓存ZSet
*
* @param key 缓存键值
* @return 缓存数据的对象
*/
public <T> boolean setCacheZSet(final String key,
final Double score,
final T member,
final Long timeout,
final TimeUnit timeUnit) {
redisTemplate.opsForZSet().add(key, member, score);
Boolean expire = redisTemplate.expire(key, timeout, timeUnit);
return expire;
}
/**
* 根据Score值查询集合元素, 从大到小排序
*
* @param key
* @param min
* @param max
* @return
*/
public <T> Set<T> getCacheZSet(String key, double min, double max) {
return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, min, max);
}
/**
* 缓存Map
*
* @param key
* @param dataMap
*/
public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
{
if (dataMap != null) {
redisTemplate.opsForHash().putAll(key, dataMap);
}
}
/**
* 获得缓存的Map
*
* @param key
* @return
*/
public <T> Map<String, T> getCacheMap(final String key)
{
return redisTemplate.opsForHash().entries(key);
}
/**
* 往Hash中存入数据
*
* @param key Redis键
* @param hKey Hash键
* @param value 值
*/
public <T> void setCacheMapValue(final String key, final String hKey, final T value)
{
redisTemplate.opsForHash().put(key, hKey, value);
}
/**
* 获取Hash中的数据
*
* @param key Redis键
* @param hKey Hash键
* @return Hash中的对象
*/
public <T> T getCacheMapValue(final String key, final String hKey)
{
HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
return opsForHash.get(key, hKey);
}
public void deleteCacheMapValue(final String key, final String... hKey)
{
redisTemplate.opsForHash().delete(key, (Object) hKey);
}
/**
* 获取多个Hash中的数据
*
* @param key Redis键
* @param hKeys Hash键集合
* @return Hash对象集合
*/
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
{
return redisTemplate.opsForHash().multiGet(key, hKeys);
}
/**
* 获得缓存的基本对象列表
*
* @param pattern 字符串前缀
* @return 对象列表
*/
public Collection<String> keys(final String pattern)
{
return redisTemplate.keys(pattern);
}
}
4、注册一个网建短信平台的账号
网建地址:https://www.smschinese.com.cn/
注册成功之后,进入首页会赠送5条免费的短信余额,意思就是你只能发5条短信,用完之后就需要自己充值,我这边是已经用了2条之后的了
5、获取用户名和短信密钥、编辑签名
6、编写发送短信工具类
package com.cloud.system.utils;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;
import java.io.IOException;
public class SendMsgUtils {
private static final String UID = "xue17620789807";//这是建网SMS 上的登陆账号
private static final String KEY = "078A77702A25DC2EE728A00C544C5427"; //这是密钥
/**
* 手机发送短信
* @param phone 手机号码
* @param context 发送短信内容
*/
public static String send(String phone, String context) {
PostMethod post = null;
try {
//创建Http客户端
HttpClient client = new HttpClient();
//创建一个post方法
post = new PostMethod("http://utf8.api.smschinese.cn");
//添加请求头信息
post.addRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf8");//在头文件中设置转码
NameValuePair[] data = {new NameValuePair("Uid", UID),
new NameValuePair("Key", KEY),
new NameValuePair("smsMob", phone),
new NameValuePair("smsText", context)};
//设置请求体
post.setRequestBody(data);
//执行post方法
client.executeMethod(post);
//获取响应头信息
Header[] headers = post.getResponseHeaders();
//获取状态码
int statusCode = post.getStatusCode();
System.out.println("statusCode:" + statusCode);
//循环打印头信息,暂时不需要
/*for (Header h : headers) {
System.out.println(h.toString());
}*/
//获取相应体
return new String(post.getResponseBodyAsString().getBytes("utf8"));
} catch (IOException e) {
e.printStackTrace();
} finally {
if (post != null) {
//关闭资源
post.releaseConnection();
}
}
return null;
}
}
5、单元测试
package com.cloud.system;
import com.cloud.system.constant.CustomException;
import com.cloud.system.constant.VerificationConstant;
import com.cloud.system.utils.SendMsgUtils;
import com.cloud.system.utils.StrUtils;
import com.cloud.web.utils.LogUtil;
import com.cloud.web.utils.RedisUtil;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@SpringBootTest
public class AppTest {
@Resource
private RedisUtil redisUtil;
@Test
void test(){
String context=null;
try {
context = sendRegisterVerificationCode("11111111111");//电话号码
}catch (CustomException e){
System.out.println("亲!一分钟以内不能发送多次验证码!!");
}
String send = SendMsgUtils.send("1111111111", context);//电话号码和内容
if(StringUtils.isNotBlank(send)){
int count = Integer.parseInt(send);
if(count>0){
System.out.println("发送成功");
}
System.out.println(outputLog(count));
}
}
private String outputLog(int count){
switch (count){
case -1:
LogUtil.error("没有该用户账户");
break;
case -2:
LogUtil.error("接口密钥不正确");
break;
case -21:
LogUtil.error("MD5接口密钥加密不正确");
break;
case -3:
LogUtil.error("短信数量不足");
break;
case -11:
LogUtil.error("该用户被禁用");
break;
case -14:
LogUtil.error("短信内容出现非法字符");
break;
case -4:
LogUtil.error("手机号格式不正确");
return "手机号格式不正确";
case -41:
LogUtil.error("手机号码为空");
return "手机号码为空";
case -42:
LogUtil.error("短信内容为空");
break;
case -51:
LogUtil.error("短信签名格式不正确");
break;
case -52:
LogUtil.error("短信签名太长");
break;
case -6:
LogUtil.error("IP限制");
break;
default:
LogUtil.error("未知异常");
break;
}
return "发送失败";
}
private String sendRegisterVerificationCode(String phone) throws CustomException {
//随机产生4个字符
String value = StrUtils.getComplexRandomString(4);
//在redis中通过key获取对应的值 value:时间戳
String valueCode = (String) redisUtil.getCacheObject(phone + ":" + VerificationConstant.USER_REG);
//如果不为空,就意味着验证码没有过期,依然是在5分钟以内
if(org.apache.commons.lang3.StringUtils.isNotEmpty(valueCode)){
//开始时间戳
String beginTimer = valueCode.split(":")[1];
if(System.currentTimeMillis()-Long.valueOf(beginTimer)<=60*1000){
//自定义异常,自己创建一个就可以了
throw new CustomException("亲!一分钟以内不能发送多次验证码!!");
}
//重新发送之后,删除原有的验证码,采用新的
redisUtil.deleteObject(phone + ":" + VerificationConstant.USER_REG);
}
//存储redis中,设置有效期是5分钟
redisUtil.expire(phone + ":" + VerificationConstant.USER_REG,
value + ":" + System.currentTimeMillis(), 5, TimeUnit.MINUTES);
//发送手机验证码
return "尊敬的用户,您的验证码为:" + value + ", 请您在5分钟以内完成注册!!";
}
}
短信发送后返回值说明
-1 没有该用户账户
-2 接口密钥不正确
不是账户登陆密码
-21 MD5接口密钥加密不正确
-3 短信数量不足
-11 该用户被禁用
-14 短信内容出现非法字符
-4 手机号格式不正确
-41 手机号码为空
-42 短信内容为空
-51 短信签名格式不正确
-52 短信签名太长
建议签名10个字符以内
-6 IP限制
大于0 短信发送数量